diff --git a/RELEASES.md b/RELEASES.md index d0d56cfe21e..2e5ffdb3499 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -58,26 +58,36 @@ A release should not be finalized until the development team and the external co The beginning of a new major release series is marked by the release of a new major version. A major release series is comprised of all minor and patch releases made under the same major version number. The series continues to receive bug fixes (released as minor or patch releases) until it reaches end of life. The date when a major release series reaches end of life is determined by one of the two following methods: -- If the next major release is made within the first 6 months, then the end of life date of the major release series is 1 year after its initial release. -- If the next major release is made 6 months after the initial release, then the end of life date of the major release series is 6 months after the release date of the next major release. +- If the next major release is made within the first 6 months, then the end of life date of the major release series is 18 months after its initial release. +- If the next major release is made 6 months after the initial release, then the end of life date of the major release series is 12 months after the release date of the next major release. For example, if the current major release series is v1 and was released on January 1st, 2022, then v1 will be supported at least until January 1st, 2023. If v2 is published on August 1st 2022, then v1's end of life will be March 1st, 2023. Only the following major release series have a stable release status. All missing minor release versions have been discontinued. +We reserve the right to drop support for releases if they are deemed unused (for example, because the Cosmos SDK version they depend on is not used or has been deprecated). Likewise, we also reserve the right to drop support for pre v1.0 versions of modules if we deem them unnecessary to maintain (we are only looking to give support for stable major releases). + ### ibc-go |Release|End of Life Date| |-------|----------------| -|`v7.2.x`|March 17, 2024| -|`v7.3.x`|March 17, 2024| -|`v8.0.x`|November 10, 2024| +|`v7.2.x`|September 17, 2024| +|`v7.3.x`|September 17, 2024| +|`v8.0.x`|May 10, 2025| ### Callbacks middleware |Release|End of Life Date| |-------|----------------| -|`v0.1.x-ibc-go-v7.3.x`|March 17, 2024| +|`v0.1.x+ibc-go-v7.3.x`|September 17, 2024| +|`v0.1.x+ibc-go-v8.0.x`|May 10, 2025| + +### `08-wasm` light client proxy module + +|Release|End of Life Date| +|-------|----------------| +|`v0.1.0+ibc-go-v7.3.x-wasmvm-v1.5.x`|September 17, 2024| +|`v0.2.x+ibc-go-v8.0.x-wasmvm-v1.5.x`|May 10, 2025| ### What pull requests will be included in stable patch-releases? @@ -121,7 +131,17 @@ Versions of Golang, ibc-go, Cosmos SDK and CometBFT used by callbacks middleware | Go | callbacks | ibc-go | Cosmos SDK | Tendermint/CometBFT | |----|-----------|--------|------------|---------------------| -| 1.19 | v0.1.0-ibc-go-v7.3 | v7.3.0 | v0.47.4 | v0.37.2 | +| 1.19 | v0.1.0+ibc-go-v7.3 | v7.3.0 | v0.47.4 | v0.37.2 | +| 1.21 | v0.2.0+ibc-go-v8.0 | v8.0.0 | v0.50.1 | v0.38.0 | + +### `08-wasm` light client proxy module + +Versions of Golang, ibc-go, Cosmos SDK and CometBFT used by `08-wasm` module in the currently active releases: + +| Go | callbacks | ibc-go | Cosmos SDK | Tendermint/CometBFT | +|----|-----------|--------|------------|---------------------| +| 1.19 | v0.1.0+ibc-go-v7.3-wasmvm-v1.5 | v7.3.0 | v0.47.6 | v0.37.2 | +| 1.21 | v0.1.0+ibc-go-v8.0-wasmvm-v1.5 | v8.0.0 | v0.50.1 | v0.38.0 | ## Graphics diff --git a/docs/docs/01-ibc/04-middleware/02-develop.md b/docs/docs/01-ibc/04-middleware/02-develop.md index 5ce750058e1..0f6ad9ea01c 100644 --- a/docs/docs/01-ibc/04-middleware/02-develop.md +++ b/docs/docs/01-ibc/04-middleware/02-develop.md @@ -32,7 +32,7 @@ type IBCMiddleware struct { keeper keeper.Keeper } -// NewIBCMiddleware creates a new IBCMiddlware given the keeper and underlying application +// NewIBCMiddleware creates a new IBCMiddleware given the keeper and underlying application func NewIBCMiddleware(app porttypes.IBCModule, k keeper.Keeper) IBCMiddleware { return IBCMiddleware{ app: app, diff --git a/docs/docs/01-ibc/05-upgrades/00-intro.md b/docs/docs/01-ibc/05-upgrades/00-intro.md index 386a0ebfe73..61711d35f01 100644 --- a/docs/docs/01-ibc/05-upgrades/00-intro.md +++ b/docs/docs/01-ibc/05-upgrades/00-intro.md @@ -9,7 +9,7 @@ slug: /ibc/upgrades/intro This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections. -IBC-connnected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client. +IBC-connected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client. 1. The [quick-guide](./01-quick-guide.md) describes how IBC-connected chains can perform client-breaking upgrades and how relayers can securely upgrade counterparty clients using the SDK. 2. The [developer-guide](./02-developer-guide.md) is a guide for developers intending to develop IBC client implementations with upgrade functionality. diff --git a/docs/docs/01-ibc/06-channel-upgrades.md b/docs/docs/01-ibc/06-channel-upgrades.md index 5f2d965c888..f84f4cd6274 100644 --- a/docs/docs/01-ibc/06-channel-upgrades.md +++ b/docs/docs/01-ibc/06-channel-upgrades.md @@ -55,17 +55,26 @@ Each handshake step will be documented below in greater detail. ## Initializing a Channel Upgrade -A channel upgrade is initialised by submitting the `ChanUpgradeInit` message, which can be submitted only by the chain itself upon governance authorization. This message should specify an appropriate timeout window for the upgrade. It is possible to upgrade the channel ordering, the channel connection hops, and the channel version. +A channel upgrade is initialised by submitting the `MsgChannelUpgradeInit` message, which can be submitted only by the chain itself upon governance authorization. It is possible to upgrade the channel ordering, the channel connection hops, and the channel version, which can be found in the `UpgradeFields`. -As part of the handling of the `ChanUpgradeInit` message, the application's callbacks `OnChanUpgradeInit` will be triggered as well. +```go +type MsgChannelUpgradeInit struct { + PortId string + ChannelId string + Fields UpgradeFields + Signer string +} +``` + +As part of the handling of the `MsgChannelUpgradeInit` message, the application's `OnChanUpgradeInit` callback will be triggered as well. After this message is handled successfully, the channel's upgrade sequence will be incremented. This upgrade sequence will serve as a nonce for the upgrade process to provide replay protection. ### Governance gating on `ChanUpgradeInit` -The message signer for `MsgChanUpgradeInit` must be the address which has been designated as the `authority` of the `IBCKeeper`. If this proposal passes, the counterparty's channel will upgrade by default. +The message signer for `MsgChannelUpgradeInit` must be the address which has been designated as the `authority` of the `IBCKeeper`. If this proposal passes, the counterparty's channel will upgrade by default. -If chains want to initiate the upgrade of many channels, they will need to submit a governance proposal with multiple `MsgChanUpgradeInit` messages, one for each channel they would like to upgrade, again with message signer as the designated `authority` of the `IBCKeeper` +If chains want to initiate the upgrade of many channels, they will need to submit a governance proposal with multiple `MsgChannelUpgradeInit` messages, one for each channel they would like to upgrade, again with message signer as the designated `authority` of the `IBCKeeper`. The `upgrade-channels` CLI command can be used to submit a proposal that initiates the upgrade of multiple channels; see section [Upgrading channels with the CLI](#upgrading-channels-with-the-cli) below for more information. ## Channel State and Packet Flushing @@ -147,6 +156,89 @@ The application's `OnChanUpgradeRestore` callback method will be invoked. It will then be possible to re-initiate an upgrade by sending a `MsgChannelOpenInit` message. +## Timing Out a Channel Upgrade + +Timing out an outstanding channel upgrade may be necessary during the flushing packet stage of the channel upgrade process. As stated above, with `ChanUpgradeTry` or `ChanUpgradeAck`, the channel state has been changed from `OPEN` to `FLUSHING`, so no new packets will be allowed to be sent over this channel while flushing. If upgrades cannot be performed in a timely manner (due to unforeseen flushing issues), upgrade timeouts allow the channel to avoid blocking packet sends indefinitely. If flushing exceeds the time limit set in the `UpgradeTimeout` channel `Params`, the upgrade process will need to be timed out to abort the upgrade attempt and resume normal channel processing. + +Channel upgrades require setting a valid timeout value in the channel `Params` before submitting a `MsgChannelUpgradeTry` or `MsgChannelUpgradeAck` message (by default, 10 minutes): + +```go +type Params struct { + UpgradeTimeout Timeout +} +``` + +A valid Timeout contains either one or both of a timestamp and block height (sequence). + +```go +type Timeout struct { + // block height after which the packet or upgrade times out + Height types.Height + // block timestamp (in nanoseconds) after which the packet or upgrade times out + Timestamp uint64 +} +``` + +This timeout will then be set as a field on the `Upgrade` struct itself when flushing is started. + +```go +type Upgrade struct { + Fields UpgradeFields + Timeout Timeout + NextSequenceSend uint64 +} +``` + +If the timeout has been exceeded during flushing, a chain can then submit the `MsgChannelUpgradeTimeout` to timeout the channel upgrade process: + +```go +type MsgChannelUpgradeTimeout struct { + PortId string + ChannelId string + CounterpartyChannel Channel + ProofChannel []byte + ProofHeight types.Height + Signer string +} +``` + +An `ErrorReceipt` will be written with the channel's current upgrade sequence, and the channel will move back to `OPEN` state keeping its original parameters. + +The application's `OnChanUpgradeRestore` callback method will also be invoked. + +Note that timing out a channel upgrade will end the upgrade process, and a new `MsgChannelUpgradeInit` will have to be submitted via governance in order to restart the upgrade process. + +## Pruning Acknowledgements + +Acknowledgements can be pruned by broadcasting the `MsgPruneAcknowledgements` message. + +> Note: It is only possible to prune acknowledgements after a channel has been upgraded, so pruning will fail +> if the channel has not yet been upgraded. + +```protobuf +// MsgPruneAcknowledgements defines the request type for the PruneAcknowledgements rpc. +message MsgPruneAcknowledgements { + option (cosmos.msg.v1.signer) = "signer"; + option (gogoproto.goproto_getters) = false; + + string port_id = 1; + string channel_id = 2; + uint64 limit = 3; + string signer = 4; +} +``` + +The `port_id` and `channel_id` specify the port and channel to act on, and the `limit` specifies the upper bound for the number +of acknowledgements and packet receipts to prune. + +### CLI Usage + +Acknowledgements can be pruned via the cli with the `prune-acknowledgements` command. + +```bash +simd tx ibc channel prune-acknowledgements [port] [channel] [limit] +``` + ## IBC App Recommendations IBC application callbacks should be primarily used to validate data fields and do compatibility checks. @@ -164,3 +256,135 @@ IBC application callbacks should be primarily used to validate data fields and d > IBC applications should not attempt to process any packet data under the new conditions until after `OnChanUpgradeOpen` > has been executed, as up until this point it is still possible for the upgrade handshake to fail and for the channel > to remain in the pre-upgraded state. + +## Upgrade an existing transfer application stack to use 29-fee middleware + +### Wire up the transfer stack and middleware in app.go + +In app.go, the existing transfer stack must be wrapped with the fee middleware. + +```golang + +import ( + // ... + ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee" + ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" + transfer "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" + // ... +) + +type App struct { + // ... + TransferKeeper ibctransferkeeper.Keeper + IBCFeeKeeper ibcfeekeeper.Keeper + // .. +} + +// ... + +app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + appCodec, keys[ibcfeetypes.StoreKey], + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, +) + +// Create Transfer Keeper and pass IBCFeeKeeper as expected Channel and PortKeeper +// since fee middleware will wrap the IBCKeeper for underlying application. +app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCFeeKeeper, // ISC4 Wrapper: fee IBC middleware + app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper, + app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), +) + + +ibcRouter := porttypes.NewRouter() + +// create IBC module from bottom to top of stack +var transferStack porttypes.IBCModule +transferStack = transfer.NewIBCModule(app.TransferKeeper) +transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper) + +// Add transfer stack to IBC Router +ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) +``` + +### Submit a governance proposal to execute a MsgChannelUpgradeInit message + +> This process can be performed with the new CLI that has been added +> outlined [here](#upgrading-channels-with-the-cli). + +Only the configured authority for the ibc module is able to initiate a channel upgrade by submitting a `MsgChannelUpgradeInit` message. + +Execute a governance proposal specifying the relevant fields to perform a channel upgrade. + +Update the following json sample, and copy the contents into `proposal.json`. + +```json +{ + "title": "Channel upgrade init", + "summary": "Channel upgrade init", + "messages": [ + { + "@type": "/ibc.core.channel.v1.MsgChannelUpgradeInit", + "signer": "", + "port_id": "transfer", + "channel_id": "channel-...", + "fields": { + "ordering": "ORDER_UNORDERED", + "connection_hops": ["connection-0"], + "version": "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" + } + } + ], + "metadata": "", + "deposit": "10stake" +} +``` + +> Note: ensure the correct fields.version is specified. This is the new version that the channels will be upgraded to. + +### Submit the proposal + +```shell +simd tx submit-proposal proposal.json --from +``` + +## Upgrading channels with the CLI + +A new cli has been added which enables either + - submitting a governance proposal which contains a `MsgChannelUpgradeInit` for every channel to be upgraded. + - generating a `proposal.json` file which contains the proposal contents to be edited/submitted at a later date. + +The following example, would submit a governance proposal with the specified deposit, title and summary which would +contain a `MsgChannelUpgradeInit` for all `OPEN` channels whose port matches the regular expression `transfer`. + +> Note: by adding the `--json` flag, the command would instead output the contents of the proposal which could be +> stored in a `proposal.json` file to be edited and submitted at a later date. + +```bash +simd tx ibc channel upgrade-channels "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" \ + --deposit "10stake" \ + --title "Channel Upgrades Governance Proposal" \ + --summary "Upgrade all transfer channels to be fee enabled" \ + --port-pattern "transfer" +``` + +It is also possible to explicitly list a comma separated string of channel IDs. It is important to note that the +regular expression matching specified by `--port-pattern` (which defaults to `transfer`) still applies. + +For example the following command would generate the contents of a `proposal.json` file which would attempt to upgrade +channels with a port ID of `transfer` and a channelID of `channel-0`, `channel-1` or `channel-2`. + +```bash +simd tx ibc channel upgrade-channels "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" \ + --deposit "10stake" \ + --title "Channel Upgrades Governance Proposal" \ + --summary "Upgrade all transfer channels to be fee enabled" \ + --port-pattern "transfer" \ + --channel-ids "channel-0,channel-1,channel-2" \ + --json +``` diff --git a/docs/docs/01-ibc/10-roadmap.md b/docs/docs/01-ibc/10-roadmap.md index ee09125ee33..962108bbde7 100644 --- a/docs/docs/01-ibc/10-roadmap.md +++ b/docs/docs/01-ibc/10-roadmap.md @@ -7,7 +7,7 @@ slug: /ibc/roadmap # Roadmap ibc-go -*Lastest update: December 4th, 2023* +*Latest update: December 4th, 2023* This document endeavours to inform the wider IBC community about plans and priorities for work on ibc-go by the team at Interchain GmbH. It is intended to broadly inform all users of ibc-go, including developers and operators of IBC, relayer, chain and wallet applications. diff --git a/docs/docs/01-ibc/11-troubleshooting.md b/docs/docs/01-ibc/11-troubleshooting.md index ba9dde39256..4080f3b449b 100644 --- a/docs/docs/01-ibc/11-troubleshooting.md +++ b/docs/docs/01-ibc/11-troubleshooting.md @@ -12,4 +12,4 @@ slug: /ibc/troubleshooting If it is being reported that a client state is unauthorized, this is due to the client type not being present in the [`AllowedClients`](https://github.com/cosmos/ibc-go/blob/v6.0.0/modules/core/02-client/types/client.pb.go#L345) array. -Unless the client type is present in this array, all usage of clients of this type will be prevented. +Unless the client type is present in this array or the `AllowAllClients` wildcard (`"*"`) is used, all usage of clients of this type will be prevented. 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 904a53262d1..31481ea7642 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 @@ -131,3 +131,5 @@ where `proposal.json` contains: "deposit": "100stake" } ``` + +If the `AllowedClients` list contains a single element that is equal to the wildcard `"*"`, then all client types are allowed and it is thus not necessary to submit a governance proposal to update the parameter. diff --git a/docs/docs/03-light-clients/02-localhost/02-integration.md b/docs/docs/03-light-clients/02-localhost/02-integration.md index 01f77fce67e..e1fc491a332 100644 --- a/docs/docs/03-light-clients/02-localhost/02-integration.md +++ b/docs/docs/03-light-clients/02-localhost/02-integration.md @@ -10,11 +10,10 @@ slug: /ibc/light-clients/localhost/integration The 09-localhost light client module registers codec types within the core IBC module. This differs from other light client module implementations which are expected to register codec types using the `AppModuleBasic` interface. -The localhost client is added to the 02-client submodule param [`allowed_clients`](https://github.com/cosmos/ibc-go/blob/v7.0.0/proto/ibc/core/client/v1/client.proto#L102) by default in ibc-go. +The localhost client is implicitly enabled by using the `AllowAllClients` wildcard (`"*"`) in the 02-client submodule default value for param [`allowed_clients`](https://github.com/cosmos/ibc-go/blob/v7.0.0/proto/ibc/core/client/v1/client.proto#L102). ```go -var ( - // DefaultAllowedClients are the default clients for the AllowedClients parameter. - DefaultAllowedClients = []string{exported.Solomachine, exported.Tendermint, exported.Localhost} -) +// DefaultAllowedClients are the default clients for the AllowedClients parameter. +// By default it allows all client types. +var DefaultAllowedClients = []string{AllowAllClients} ``` 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 466991bf9e5..3827203e6cd 100644 --- a/docs/docs/03-light-clients/04-wasm/03-integration.md +++ b/docs/docs/03-light-clients/04-wasm/03-integration.md @@ -307,7 +307,7 @@ app.WasmClientKeeper = ibcwasmkeeper.NewKeeperWithVM( ## Updating `AllowedClients` -In order to use the `08-wasm` module chains must update the [`AllowedClients` parameter in the 02-client submodule](https://github.com/cosmos/ibc-go/blob/v8.0.0/proto/ibc/core/client/v1/client.proto#L64) of core IBC. This can be configured directly in the application upgrade handler with the sample code below: +If the chain's 02-client submodule parameter `AllowedClients` contains the single wildcard `"*"` element, then it is not necessary to do anything in order to allow the creation of `08-wasm` clients. However, if the parameter contains a list of client types (e.g. `["06-solomachine", "07-tendermint"]`), then in order to use the `08-wasm` module chains must update the [`AllowedClients` parameter](https://github.com/cosmos/ibc-go/blob/v8.0.0/proto/ibc/core/client/v1/client.proto#L64) of core IBC. This can be configured directly in the application upgrade handler with the sample code below: ```go import ( diff --git a/docs/params/params.md b/docs/params/params.md index f588e09f413..d67191606d1 100644 --- a/docs/params/params.md +++ b/docs/params/params.md @@ -8,14 +8,17 @@ slug: /params.md The 02-client submodule contains the following parameters: -| Key | Type | Default Value | -| ---------------- | -------- | ------------------------------------------------- | -| `AllowedClients` | []string | `"06-solomachine","07-tendermint","09-localhost"` | +| Key | Type | Default Value | +| ---------------- | -------- | ------------- | +| `AllowedClients` | []string | `"*"` | ### AllowedClients -The allowed clients parameter defines an allowlist of client types supported by the chain. A client -that is not registered on this list will fail upon creation or on genesis validation. Note that, -since the client type is an arbitrary string, chains they must not register two light clients which -return the same value for the `ClientType()` function, otherwise the allowlist check can be +The allowed clients parameter defines an allow list of client types supported by the chain. The +default value is a single-element list containing the `AllowAllClients` wildcard (`"*"`). When the +wilcard is used, then all client types are supported by default. Alternatively, the parameter +may be set with a list of client types (e.g. `"06-solomachine","07-tendermint","09-localhost"`). +A client type that is not registered on this list will fail upon creation or on genesis validation. +Note that, since the client type is an arbitrary string, chains must not register two light clients +which return the same value for the `ClientType()` function, otherwise the allow list check can be bypassed. diff --git a/docs/requirements/channel-upgradability-requirements.md b/docs/requirements/channel-upgradability-requirements.md index 78cf9e6bdb3..87093b989b6 100644 --- a/docs/requirements/channel-upgradability-requirements.md +++ b/docs/requirements/channel-upgradability-requirements.md @@ -129,7 +129,7 @@ Sample exception flows: | ID | Description | Verification | Status | | -- | ----------- | ------------ | ------ | -| 1.01 | An on-chain parameter keeps a list of all connection IDs (e.g. [`connection-0`, `connection-1`]) for which channels are allowed to be upgraded for an upgrade proposed on a counterpary chain | TBD | `Drafted` | +| 1.01 | An on-chain parameter keeps a list of all connection IDs (e.g. [`connection-0`, `connection-1`]) for which channels are allowed to be upgraded for an upgrade proposed on a counterparty chain | TBD | `Drafted` | | 1.02 | The on-chain parameter of connection IDs can only be updated by an authorized actor (e.g. governance) | TBD | `Drafted` | ### 2 - Initiation @@ -138,7 +138,7 @@ Sample exception flows: | -- | ----------- | ------------ | ------ | | 2.01 | An upgrade initiated by an authorized actor (e.g. governance) is always allowed | TBD | `Drafted` | | 2.02 | A chain can configure a channel upgrade to be initiated automatically after a successful governance proposal | TBD | `Drafted` | -| 2.03 | After permission is granted for channels in a given connection to be upgraded, any relayer can continue the upgrade proposed on a counterpary chain | TBD |`Drafted` | +| 2.03 | After permission is granted for channels in a given connection to be upgraded, any relayer can continue the upgrade proposed on a counterparty chain | TBD |`Drafted` | | 2.04 | A channel upgrade will be initiated when both `ChannelEnd`s are in the `OPEN` state | TBD | `Drafted` | | 2.05 | In the case of a crossing hello, a channel upgrade can be initiated when the counterparty has also executed the `ChanUpgradeInit` datagram with compatible parameters in the case of a crossing hello | TBD | `Drafted` | diff --git a/e2e/go.mod b/e2e/go.mod index d17ef6ff759..6acd571658c 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -3,7 +3,7 @@ module github.com/cosmos/ibc-go/e2e go 1.21 require ( - cosmossdk.io/errors v1.0.0 + cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.2.0 cosmossdk.io/x/upgrade v0.1.1 github.com/cometbft/cometbft v0.38.2 @@ -31,8 +31,8 @@ require ( cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core v0.11.0 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect - cosmossdk.io/log v1.2.1 // indirect - cosmossdk.io/store v1.0.1 // indirect + cosmossdk.io/log v1.3.0 // indirect + cosmossdk.io/store v1.0.2 // indirect cosmossdk.io/x/circuit v0.1.0 // indirect cosmossdk.io/x/evidence v0.1.0 // indirect cosmossdk.io/x/feegrant v0.1.0 // indirect @@ -44,7 +44,7 @@ require ( github.com/ChainSafe/go-schnorrkel v1.1.0 // indirect github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect - github.com/CosmWasm/wasmvm v1.5.0 // indirect + github.com/CosmWasm/wasmvm v1.5.1 // indirect github.com/DataDog/datadog-go v4.8.3+incompatible // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 665af39e114..bf319c460ec 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -194,14 +194,14 @@ cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= -cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= -cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= -cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= -cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= -cosmossdk.io/store v1.0.1 h1:XBDhCqlL+2MUgE8CHWwndKVJ4beX+TyaPIjB5SV62dM= -cosmossdk.io/store v1.0.1/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= cosmossdk.io/x/circuit v0.1.0/go.mod h1:YDzblVE8+E+urPYQq5kq5foRY/IzhXovSYXb4nwd39w= cosmossdk.io/x/evidence v0.1.0 h1:J6OEyDl1rbykksdGynzPKG5R/zm6TacwW2fbLTW4nCk= @@ -231,8 +231,8 @@ github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRr github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= -github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= -github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= +github.com/CosmWasm/wasmvm v1.5.1 h1:2MHN9uFyHP6pxfvpBJ0JW6ujvAIBk9kQk283zyri0Ro= +github.com/CosmWasm/wasmvm v1.5.1/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index cb44b549a50..e2fd5cb4570 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -118,6 +118,64 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { }) } +// TestChannelUpgrade_WithFeeMiddleware_FailsWithTimeoutOnAck tests upgrading a transfer channel to wire up fee middleware but fails on ACK because of timeout +func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_FailsWithTimeoutOnAck() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, s.TransferChannelOptions()) + channelB := channelA.Counterparty + chainA, chainB := s.GetChains() + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("execute gov proposal to set upgrade timeout", func(t *testing.T) { + s.setUpgradeTimeoutParam(ctx, chainB, chainBWallet) + }) + + t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { + chA, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.initiateChannelUpgrade(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, s.createUpgradeFields(chA)) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") + + t.Run("verify channel A did not upgrade", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + + errorReceipt, err := s.QueryUpgradeError(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Equal(uint64(1), errorReceipt.Sequence) + s.Require().Contains(errorReceipt.Message, "restored channel to pre-upgrade state") + }) + + t.Run("verify channel B did not upgrade", func(t *testing.T) { + channel, err := s.QueryChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + + s.Require().Equal(channeltypes.OPEN, channel.State, "the channel state is not OPEN") + s.Require().Equal(transfertypes.Version, channel.Version, "the channel version is not ics20-1") + + errorReceipt, err := s.QueryUpgradeError(ctx, chainB, channelB.PortID, channelB.ChannelID) + s.Require().NoError(err) + s.Require().Equal(uint64(1), errorReceipt.Sequence) + s.Require().Contains(errorReceipt.Message, "restored channel to pre-upgrade state") + }) +} + // createUpgradeFields created the upgrade fields for channel func (s *ChannelTestSuite) createUpgradeFields(channel channeltypes.Channel) channeltypes.UpgradeFields { versionMetadata := feetypes.Metadata{ @@ -130,6 +188,18 @@ func (s *ChannelTestSuite) createUpgradeFields(channel channeltypes.Channel) cha return channeltypes.NewUpgradeFields(channel.Ordering, channel.ConnectionHops, string(versionBytes)) } +// setUpgradeTimeoutParam creates and submits a governance proposal to execute the message to update 04-channel params with a timeout of 1s +func (s *ChannelTestSuite) setUpgradeTimeoutParam(ctx context.Context, chain ibc.Chain, wallet ibc.Wallet) { + const timeoutDelta = 1000000000 // use 1 second as relative timeout to force upgrade timeout on the counterparty + govModuleAddress, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chain) + s.Require().NoError(err) + s.Require().NotNil(govModuleAddress) + + upgradeTimeout := channeltypes.NewTimeout(channeltypes.DefaultTimeout.Height, timeoutDelta) + msg := channeltypes.NewMsgUpdateChannelParams(govModuleAddress.String(), channeltypes.NewParams(upgradeTimeout)) + s.ExecuteAndPassGovV1Proposal(ctx, msg, chain, wallet) +} + // initiateChannelUpgrade creates and submits a governance proposal to execute the message to initiate a channel upgrade func (s *ChannelTestSuite) initiateChannelUpgrade(ctx context.Context, chain ibc.Chain, wallet ibc.Wallet, portID, channelID string, upgradeFields channeltypes.UpgradeFields) { govModuleAddress, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chain) diff --git a/e2e/tests/wasm/grandpa_test.go b/e2e/tests/wasm/grandpa_test.go index 1db15f28d20..b67a4c930e7 100644 --- a/e2e/tests/wasm/grandpa_test.go +++ b/e2e/tests/wasm/grandpa_test.go @@ -23,9 +23,6 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - paramsproposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - - cmtjson "github.com/cometbft/cometbft/libs/json" "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" @@ -128,11 +125,6 @@ func (s *GrandpaTestSuite) TestMsgTransfer_Succeeds_GrandpaContract() { err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) s.Require().NoError(err) - t.Run("add 08-wasm to list of allowed clients", func(t *testing.T) { - allowedClients := s.queryAllowedClientsParam(ctx, cosmosChain) - s.allowWasmClients(ctx, cosmosChain, cosmosWallet, allowedClients) - }) - // Create new clients err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts()) s.Require().NoError(err) @@ -285,11 +277,6 @@ func (s *GrandpaTestSuite) TestMsgTransfer_TimesOut_GrandpaContract() { err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) s.Require().NoError(err) - t.Run("add 08-wasm to list of allowed clients", func(t *testing.T) { - allowedClients := s.queryAllowedClientsParam(ctx, cosmosChain) - s.allowWasmClients(ctx, cosmosChain, cosmosWallet, allowedClients) - }) - // Create new clients err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts()) s.Require().NoError(err) @@ -354,7 +341,6 @@ func (s *GrandpaTestSuite) TestMsgTransfer_TimesOut_GrandpaContract() { // * Migrates the wasm client contract func (s *GrandpaTestSuite) TestMsgMigrateContract_Success_GrandpaContract() { ctx := context.Background() - t := s.T() chainA, chainB := s.GetGrandpaTestChains() @@ -392,11 +378,6 @@ func (s *GrandpaTestSuite) TestMsgMigrateContract_Success_GrandpaContract() { err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) s.Require().NoError(err) - t.Run("add 08-wasm to list of allowed clients", func(t *testing.T) { - allowedClients := s.queryAllowedClientsParam(ctx, cosmosChain) - s.allowWasmClients(ctx, cosmosChain, cosmosWallet, allowedClients) - }) - // Create new clients err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts()) s.Require().NoError(err) @@ -447,7 +428,6 @@ func (s *GrandpaTestSuite) TestMsgMigrateContract_Success_GrandpaContract() { // * Migrates the wasm client contract with a contract that will always fail migration func (s *GrandpaTestSuite) TestMsgMigrateContract_ContractError_GrandpaContract() { ctx := context.Background() - t := s.T() chainA, chainB := s.GetGrandpaTestChains() @@ -484,11 +464,6 @@ func (s *GrandpaTestSuite) TestMsgMigrateContract_ContractError_GrandpaContract( err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) s.Require().NoError(err) - t.Run("add 08-wasm to list of allowed clients", func(t *testing.T) { - allowedClients := s.queryAllowedClientsParam(ctx, cosmosChain) - s.allowWasmClients(ctx, cosmosChain, cosmosWallet, allowedClients) - }) - // Create new clients err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts()) s.Require().NoError(err) @@ -541,7 +516,6 @@ func (s *GrandpaTestSuite) TestMsgMigrateContract_ContractError_GrandpaContract( // This contract modifies the unbonding period to 1600s with the trusting period being calculated as (unbonding period / 3). func (s *GrandpaTestSuite) TestRecoverClient_Succeeds_GrandpaContract() { ctx := context.Background() - t := s.T() // set the trusting period to a value which will still be valid upon client creation, but invalid before the first update // the contract uses 1600s as the unbonding period with the trusting period evaluating to (unbonding period / 3) @@ -585,11 +559,6 @@ func (s *GrandpaTestSuite) TestRecoverClient_Succeeds_GrandpaContract() { err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName) s.Require().NoError(err) - t.Run("add 08-wasm to list of allowed clients", func(t *testing.T) { - allowedClients := s.queryAllowedClientsParam(ctx, cosmosChain) - s.allowWasmClients(ctx, cosmosChain, cosmosWallet, allowedClients) - }) - // create client pair with subject (bad trusting period) subjectClientID := clienttypes.FormatClientIdentifier(wasmtypes.Wasm, 0) // TODO: The hyperspace relayer makes no use of create client opts @@ -797,48 +766,3 @@ func (s *GrandpaTestSuite) GetGrandpaTestChains() (ibc.Chain, ibc.Chain) { options.ChainBSpec.EncodingConfig = testsuite.SDKEncodingConfig() }) } - -// queryAllowedClientsParam queries the on-chain allowed clients param for 02-client -func (s *GrandpaTestSuite) queryAllowedClientsParam(ctx context.Context, chain ibc.Chain) []string { - if testvalues.SelfParamsFeatureReleases.IsSupported(chain.Config().Images[0].Version) { - queryClient := s.GetChainGRCPClients(chain).ClientQueryClient - res, err := queryClient.ClientParams(ctx, &clienttypes.QueryClientParamsRequest{}) - s.Require().NoError(err) - - return res.Params.AllowedClients - } - queryClient := s.GetChainGRCPClients(chain).ParamsQueryClient - res, err := queryClient.Params(ctx, ¶msproposaltypes.QueryParamsRequest{ - Subspace: ibcexported.ModuleName, - Key: string(clienttypes.KeyAllowedClients), - }) - s.Require().NoError(err) - - var allowedClients []string - err = cmtjson.Unmarshal([]byte(res.Param.Value), &allowedClients) - s.Require().NoError(err) - - return allowedClients -} - -// allowWasmClients adds 08-wasm to the on-chain allowed clients param for 02-client -func (s *GrandpaTestSuite) allowWasmClients(ctx context.Context, chain ibc.Chain, wallet ibc.Wallet, allowedClients []string) { - govModuleAddress, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chain) - s.Require().NoError(err) - s.Require().NotNil(govModuleAddress) - - allowedClients = append(allowedClients, wasmtypes.Wasm) - if testvalues.SelfParamsFeatureReleases.IsSupported(chain.Config().Images[0].Version) { - msg := clienttypes.NewMsgUpdateParams(govModuleAddress.String(), clienttypes.NewParams(allowedClients...)) - s.ExecuteAndPassGovV1Proposal(ctx, msg, chain, wallet) - } else { - value, err := cmtjson.Marshal(allowedClients) - s.Require().NoError(err) - changes := []paramsproposaltypes.ParamChange{ - paramsproposaltypes.NewParamChange(ibcexported.ModuleName, string(clienttypes.KeyAllowedClients), string(value)), - } - - proposal := paramsproposaltypes.NewParameterChangeProposal(ibctesting.Title, ibctesting.Description, changes) - s.ExecuteAndPassGovV1Beta1Proposal(ctx, chain, wallet, proposal) - } -} diff --git a/e2e/testsuite/grpc_query.go b/e2e/testsuite/grpc_query.go index 23e9a5fa5f8..107306db7e0 100644 --- a/e2e/testsuite/grpc_query.go +++ b/e2e/testsuite/grpc_query.go @@ -214,6 +214,19 @@ func (s *E2ETestSuite) QueryPacketCommitment(ctx context.Context, chain ibc.Chai return res.Commitment, nil } +// QueryUpgradeError queries the upgrade error on the given chain for the provided channel. +func (s *E2ETestSuite) QueryUpgradeError(ctx context.Context, chain ibc.Chain, portID, channelID string) (channeltypes.ErrorReceipt, error) { + queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient + res, err := queryClient.UpgradeError(ctx, &channeltypes.QueryUpgradeErrorRequest{ + PortId: portID, + ChannelId: channelID, + }) + if err != nil { + return channeltypes.ErrorReceipt{}, err + } + return res.ErrorReceipt, nil +} + // QueryTotalEscrowForDenom queries the total amount of tokens in escrow for a denom func (s *E2ETestSuite) QueryTotalEscrowForDenom(ctx context.Context, chain ibc.Chain, denom string) (sdk.Coin, error) { queryClient := s.GetChainGRCPClients(chain).TransferQueryClient diff --git a/e2e/testsuite/testconfig.go b/e2e/testsuite/testconfig.go index c88646fc6d9..f8cfe1b5b7a 100644 --- a/e2e/testsuite/testconfig.go +++ b/e2e/testsuite/testconfig.go @@ -27,6 +27,10 @@ import ( "github.com/cosmos/ibc-go/e2e/relayer" "github.com/cosmos/ibc-go/e2e/semverutil" "github.com/cosmos/ibc-go/e2e/testvalues" + wasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibctypes "github.com/cosmos/ibc-go/v8/modules/core/types" ) const ( @@ -532,7 +536,7 @@ func getGenesisModificationFunction(cc ChainConfig) func(ibc.ChainConfig, []byte return defaultGovv1ModifyGenesis(version) } - return defaultGovv1Beta1ModifyGenesis() + return defaultGovv1Beta1ModifyGenesis(version) } // defaultGovv1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit @@ -554,9 +558,16 @@ func defaultGovv1ModifyGenesis(version string) func(ibc.ChainConfig, []byte) ([] if err != nil { return nil, err } - appState[govtypes.ModuleName] = govGenBz + if !testvalues.AllowAllClientsWildcardFeatureReleases.IsSupported(version) { + ibcGenBz, err := modifyClientGenesisAppState(chainConfig, appState[ibcexported.ModuleName]) + if err != nil { + return nil, err + } + appState[ibcexported.ModuleName] = ibcGenBz + } + appGenesis.AppState, err = json.Marshal(appState) if err != nil { return nil, err @@ -581,7 +592,7 @@ func defaultGovv1ModifyGenesis(version string) func(ibc.ChainConfig, []byte) ([] // defaultGovv1Beta1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit // // are functional for e2e testing purposes. -func defaultGovv1Beta1ModifyGenesis() func(ibc.ChainConfig, []byte) ([]byte, error) { +func defaultGovv1Beta1ModifyGenesis(version string) func(ibc.ChainConfig, []byte) ([]byte, error) { const appStateKey = "app_state" return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { genesisDocMap := map[string]interface{}{} @@ -611,6 +622,24 @@ func defaultGovv1Beta1ModifyGenesis() func(ibc.ChainConfig, []byte) ([]byte, err return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err) } + if !testvalues.AllowAllClientsWildcardFeatureReleases.IsSupported(version) { + ibcModuleBytes, err := json.Marshal(appStateMap[ibcexported.ModuleName]) + if err != nil { + return nil, fmt.Errorf("failed to extract ibc genesis bytes: %s", err) + } + + ibcGenesisBytes, err := modifyClientGenesisAppState(chainConfig, ibcModuleBytes) + if err != nil { + return nil, err + } + + ibcModuleGenesisMap := map[string]interface{}{} + err = json.Unmarshal(ibcGenesisBytes, &ibcModuleGenesisMap) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err) + } + } + appStateMap[govtypes.ModuleName] = govModuleGenesisMap genesisDocMap[appStateKey] = appStateMap @@ -673,3 +702,24 @@ func modifyGovv1Beta1AppState(chainConfig ibc.ChainConfig, govAppState []byte) ( return govGenBz, nil } + +// modifyClientGenesisAppState takes the existing ibc app state and marshals it to a ibc GenesisState. +func modifyClientGenesisAppState(chainConfig ibc.ChainConfig, ibcAppState []byte) ([]byte, error) { + cfg := testutil.MakeTestEncodingConfig() + + cdc := codec.NewProtoCodec(cfg.InterfaceRegistry) + clienttypes.RegisterInterfaces(cfg.InterfaceRegistry) + + ibcGenesisState := &ibctypes.GenesisState{} + if err := cdc.UnmarshalJSON(ibcAppState, ibcGenesisState); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis bytes into client genesis state: %w", err) + } + + ibcGenesisState.ClientGenesis.Params.AllowedClients = append(ibcGenesisState.ClientGenesis.Params.AllowedClients, wasmtypes.Wasm) + ibcGenBz, err := cdc.MarshalJSON(ibcGenesisState) + if err != nil { + return nil, fmt.Errorf("failed to marshal gov genesis state: %w", err) + } + + return ibcGenBz, nil +} diff --git a/e2e/testvalues/values.go b/e2e/testvalues/values.go index a700c217e02..bab1aee577e 100644 --- a/e2e/testvalues/values.go +++ b/e2e/testvalues/values.go @@ -91,3 +91,11 @@ var LocalhostClientFeatureReleases = semverutil.FeatureReleases{ "v7.1", }, } + +// AllowAllClientsWildcardFeatureReleases represents the releases the allow all clients wildcard was released in. +var AllowAllClientsWildcardFeatureReleases = semverutil.FeatureReleases{ + MajorVersion: "v9", + MinorVersions: []string{ + "v8.1", + }, +} diff --git a/go.mod b/go.mod index ca27a6be576..50c6df59ac8 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( cosmossdk.io/api v0.7.2 cosmossdk.io/client/v2 v2.0.0-beta.1 cosmossdk.io/core v0.11.0 - cosmossdk.io/errors v1.0.0 - cosmossdk.io/log v1.2.1 + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.0 cosmossdk.io/math v1.2.0 - cosmossdk.io/store v1.0.1 + cosmossdk.io/store v1.0.2 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/circuit v0.1.0 cosmossdk.io/x/evidence v0.1.0 @@ -30,7 +30,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f google.golang.org/grpc v1.60.1 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v2 v2.4.0 @@ -184,8 +184,8 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.153.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect diff --git a/go.sum b/go.sum index 5e635b2874c..8a716140404 100644 --- a/go.sum +++ b/go.sum @@ -194,14 +194,14 @@ cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= -cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= -cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= -cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= -cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= -cosmossdk.io/store v1.0.1 h1:XBDhCqlL+2MUgE8CHWwndKVJ4beX+TyaPIjB5SV62dM= -cosmossdk.io/store v1.0.1/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/tools/confix v0.1.1 h1:aexyRv9+y15veH3Qw16lxQwo+ki7r2I+g0yNTEFEQM8= cosmossdk.io/tools/confix v0.1.1/go.mod h1:nQVvP1tHsGXS83PonPVWJtSbddIqyjEw99L4M3rPJyQ= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= @@ -1578,12 +1578,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 h1:kzJAXnzZoFbe5bhZd4zjUuHos/I31yH4thfMb/13oVY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/go.work.example b/go.work.example index 07249434f5a..a9af0dd205d 100644 --- a/go.work.example +++ b/go.work.example @@ -1,4 +1,4 @@ -go 1.20 +go 1.21 use ( ./ diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go index 199c4f2d9da..ae05b7476b2 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware.go @@ -233,12 +233,12 @@ func (im IBCMiddleware) OnTimeoutPacket( } // OnChanUpgradeInit implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) (string, error) { +func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) (string, error) { if !im.keeper.GetParams(ctx).ControllerEnabled { return "", types.ErrControllerSubModuleDisabled } - version, err := im.keeper.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, version) + proposedVersion, err := im.keeper.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) if err != nil { return "", err } @@ -254,14 +254,14 @@ func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID str if !ok { return "", errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack") } - return cbs.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, version) + return cbs.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } - return version, nil + return proposedVersion, nil } // OnChanUpgradeTry implements the IBCModule interface -func (IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +func (IBCMiddleware) OnChanUpgradeTry(_ sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { return "", errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel upgrade handshake must be initiated by controller chain") } @@ -293,7 +293,7 @@ func (im IBCMiddleware) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, cou } // OnChanUpgradeOpen implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { connectionID, err := im.keeper.GetConnectionID(ctx, portID, channelID) if err != nil { panic(err) @@ -305,7 +305,7 @@ func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID str if !ok { panic(errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack")) } - cbs.OnChanUpgradeOpen(ctx, portID, channelID, order, connectionHops, version) + cbs.OnChanUpgradeOpen(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } } diff --git a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go index 17234cc62a9..8d688efb678 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go @@ -155,11 +155,11 @@ func (Keeper) OnChanCloseConfirm( // - connectionHops (and subsequently host/controller connectionIDs) // - interchain account address // - ICS27 protocol version -func (k Keeper) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) (string, error) { +func (k Keeper) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedversion string) (string, error) { // verify order has not changed // support for unordered ICA channels is not implemented yet - if order != channeltypes.ORDERED { - return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) + if proposedOrder != channeltypes.ORDERED { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, proposedOrder) } // verify connection hops has not changed @@ -168,16 +168,16 @@ func (k Keeper) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, ord return "", err } - if len(connectionHops) != 1 || connectionHops[0] != connectionID { - return "", errorsmod.Wrapf(channeltypes.ErrInvalidUpgrade, "expected connection hops %s, got %s", []string{connectionID}, connectionHops) + if len(proposedConnectionHops) != 1 || proposedConnectionHops[0] != connectionID { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidUpgrade, "expected connection hops %s, got %s", []string{connectionID}, proposedConnectionHops) } // verify proposed version only modifies tx type or encoding - if strings.TrimSpace(version) == "" { + if strings.TrimSpace(proposedversion) == "" { return "", errorsmod.Wrap(icatypes.ErrInvalidVersion, "version cannot be empty") } - proposedMetadata, err := icatypes.MetadataFromVersion(version) + proposedMetadata, err := icatypes.MetadataFromVersion(proposedversion) if err != nil { return "", err } @@ -189,7 +189,7 @@ func (k Keeper) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, ord // ValidateControllerMetadata will ensure the ICS27 protocol version has not changed and that the // tx type and encoding are supported - if err := icatypes.ValidateControllerMetadata(ctx, k.channelKeeper, connectionHops, proposedMetadata); err != nil { + if err := icatypes.ValidateControllerMetadata(ctx, k.channelKeeper, proposedConnectionHops, proposedMetadata); err != nil { return "", errorsmod.Wrap(err, "invalid upgrade metadata") } @@ -207,7 +207,7 @@ func (k Keeper) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, ord return "", errorsmod.Wrap(connectiontypes.ErrInvalidConnection, "proposed host connection ID must not change") } - return version, nil + return proposedversion, nil } // OnChanUpgradeAck implements the ack setup of the channel upgrade handshake. diff --git a/modules/apps/27-interchain-accounts/host/ibc_module.go b/modules/apps/27-interchain-accounts/host/ibc_module.go index bbc68506fc4..0ea90d4cddf 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module.go @@ -128,7 +128,7 @@ func (im IBCModule) OnRecvPacket( ack = channeltypes.NewErrorAcknowledgement(err) logger.Error(fmt.Sprintf("%s sequence %d", err.Error(), packet.Sequence)) } else { - logger.Info("successfully handled packet sequence: %d", packet.Sequence) + logger.Info("successfully handled packet", "sequence", packet.Sequence) } // Emit an event indicating a successful or failed acknowledgement. @@ -158,17 +158,17 @@ func (IBCModule) OnTimeoutPacket( } // OnChanUpgradeInit implements the IBCModule interface -func (IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) (string, error) { +func (IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) (string, error) { return "", errorsmod.Wrap(icatypes.ErrInvalidChannelFlow, "channel upgrade handshake must be initiated by controller chain") } // OnChanUpgradeTry implements the IBCModule interface -func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { if !im.keeper.GetParams(ctx).HostEnabled { return "", types.ErrHostSubModuleDisabled } - return im.keeper.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion) + return im.keeper.OnChanUpgradeTry(ctx, portID, channelID, proposedOrder, proposedConnectionHops, counterpartyVersion) } // OnChanUpgradeAck implements the IBCModule interface @@ -177,7 +177,7 @@ func (IBCModule) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, counterpar } // OnChanUpgradeOpen implements the IBCModule interface -func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { } // OnChanUpgradeRestore implements the IBCModule interface diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake.go b/modules/apps/27-interchain-accounts/host/keeper/handshake.go index 819abd89426..b38f779efbf 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake.go @@ -132,21 +132,45 @@ func (Keeper) OnChanCloseConfirm( } // OnChanUpgradeTry performs the upgrade try step of the channel upgrade handshake. -func (k Keeper) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +// The upgrade try callback must verify the proposed changes to the order, connectionHops, and version. +// Within the version we have the tx type, encoding, interchain account address, host/controller connectionID's +// and the ICS27 protocol version. +// +// The following may be changed: +// - tx type (must be supported) +// - encoding (must be supported) +// +// The following may not be changed: +// - order +// - connectionHops (and subsequently host/controller connectionIDs) +// - interchain account address +// - ICS27 protocol version +func (k Keeper) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { + // verify order has not changed + // support for unordered ICA channels is not implemented yet + if proposedOrder != channeltypes.ORDERED { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, proposedOrder) + } + if portID != icatypes.HostPortID { return "", errorsmod.Wrapf(porttypes.ErrInvalidPort, "expected %s, got %s", icatypes.HostPortID, portID) } - if strings.TrimSpace(counterpartyVersion) == "" { - return "", errorsmod.Wrap(channeltypes.ErrInvalidChannelVersion, "counterparty version cannot be empty") + // verify connection hops has not changed + connectionID, err := k.getConnectionID(ctx, portID, channelID) + if err != nil { + return "", err } - // support for unordered ICA channels is not implemented yet - if order != channeltypes.ORDERED { - return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s", channeltypes.ORDERED, order) + if len(proposedConnectionHops) != 1 || proposedConnectionHops[0] != connectionID { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidUpgrade, "expected connection hops %s, got %s", []string{connectionID}, proposedConnectionHops) } - metadata, err := icatypes.MetadataFromVersion(counterpartyVersion) + if strings.TrimSpace(counterpartyVersion) == "" { + return "", errorsmod.Wrap(channeltypes.ErrInvalidChannelVersion, "counterparty version cannot be empty") + } + + proposedCounterpartyMetadata, err := icatypes.MetadataFromVersion(counterpartyVersion) if err != nil { return "", err } @@ -156,17 +180,25 @@ func (k Keeper) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, orde return "", err } - if err := icatypes.ValidateHostMetadata(ctx, k.channelKeeper, connectionHops, metadata); err != nil { + // ValidateHostMetadata will ensure the ICS27 protocol version has not changed and that the + // tx type and encoding are supported. It also validates the connection params against the counterparty metadata. + if err := icatypes.ValidateHostMetadata(ctx, k.channelKeeper, proposedConnectionHops, proposedCounterpartyMetadata); err != nil { return "", errorsmod.Wrap(err, "invalid metadata") } // the interchain account address on the host chain // must remain the same after the upgrade. - if currentMetadata.Address != metadata.Address { + if currentMetadata.Address != proposedCounterpartyMetadata.Address { return "", errorsmod.Wrap(icatypes.ErrInvalidAccountAddress, "interchain account address cannot be changed") } - if currentMetadata.HostConnectionId != connectionHops[0] { + // these explicit checks on the controller connection identifier should be unreachable + if currentMetadata.ControllerConnectionId != proposedCounterpartyMetadata.ControllerConnectionId { + return "", errorsmod.Wrap(connectiontypes.ErrInvalidConnection, "proposed controller connection ID must not change") + } + + // these explicit checks on the host connection identifier should be unreachable + if currentMetadata.HostConnectionId != proposedConnectionHops[0] { return "", errorsmod.Wrap(connectiontypes.ErrInvalidConnectionIdentifier, "proposed connection hop must not change") } diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go b/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go index c14ac85a576..bb69322d40a 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go @@ -15,6 +15,10 @@ import ( ibctesting "github.com/cosmos/ibc-go/v8/testing" ) +const ( + differentConnectionID = "connection-100" +) + // open and close channel is a helper function for TestOnChanOpenTry for reopening accounts func (suite *KeeperTestSuite) openAndCloseChannel(path *ibctesting.Path) { err := path.EndpointB.ChanOpenTry() @@ -165,6 +169,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { path.EndpointB.SetChannel(*channel) }, false, }, + { "invalid order - UNORDERED", func() { @@ -433,6 +438,15 @@ func (suite *KeeperTestSuite) TestOnChanUpgradeTry() { counterpartyVersion string ) + // updateMetadata is a helper function which modifies the metadata stored in the channel version + // and marshals it into a string to pass to OnChanUpgradeTry as the counterpartyVersion string. + updateMetadata := func(modificationFn func(*icatypes.Metadata)) { + metadata, err := icatypes.MetadataFromVersion(path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version) + suite.Require().NoError(err) + modificationFn(&metadata) + counterpartyVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&metadata)) + } + testCases := []struct { name string malleate func() @@ -450,6 +464,21 @@ func (suite *KeeperTestSuite) TestOnChanUpgradeTry() { }, expError: porttypes.ErrInvalidPort, }, + { + name: "failure: invalid order", + malleate: func() { + order = channeltypes.UNORDERED + }, + expError: channeltypes.ErrInvalidChannelOrdering, + }, + { + name: "failure: invalid proposed connectionHops", + malleate: func() { + // connection hops is provided via endpoint connectionID + path.EndpointB.ConnectionID = differentConnectionID + }, + expError: channeltypes.ErrInvalidUpgrade, + }, { name: "failure: empty counterparty version", malleate: func() { @@ -476,37 +505,54 @@ func (suite *KeeperTestSuite) TestOnChanUpgradeTry() { { name: "failure: metadata encoding not supported", malleate: func() { - metadata.Encoding = "invalid-encoding-format" - counterpartyVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&metadata)) + updateMetadata(func(metadata *icatypes.Metadata) { + metadata.Encoding = "invalid-encoding-format" + }) }, expError: icatypes.ErrInvalidCodec, }, + { + name: "failure: metadata tx type not supported", + malleate: func() { + updateMetadata(func(metadata *icatypes.Metadata) { + metadata.TxType = "invalid-tx-type" + }) + }, + expError: icatypes.ErrUnknownDataType, + }, { name: "failure: interchain account address has changed", malleate: func() { - channel := path.EndpointB.GetChannel() - metadata.Address = "invalid address" - channel.Version = string(icatypes.ModuleCdc.MustMarshalJSON(&metadata)) - path.EndpointB.SetChannel(channel) + updateMetadata(func(metadata *icatypes.Metadata) { + metadata.Address = TestOwnerAddress // use valid address + }) }, expError: icatypes.ErrInvalidAccountAddress, }, { - name: "failure: invalid connection identifier", + name: "failure: controller connection ID has changed", malleate: func() { - channel := path.EndpointB.GetChannel() - metadata.HostConnectionId = "invalid-connection-id" - channel.Version = string(icatypes.ModuleCdc.MustMarshalJSON(&metadata)) - path.EndpointB.SetChannel(channel) + updateMetadata(func(metadata *icatypes.Metadata) { + metadata.ControllerConnectionId = differentConnectionID + }) }, - expError: connectiontypes.ErrInvalidConnectionIdentifier, + expError: connectiontypes.ErrInvalidConnection, // the explicit checks on the controller connection identifier are unreachable }, { - name: "failure: invalid order", + name: "failure: host connection ID has changed", malleate: func() { - order = channeltypes.UNORDERED + updateMetadata(func(metadata *icatypes.Metadata) { + metadata.HostConnectionId = differentConnectionID + }) }, - expError: channeltypes.ErrInvalidChannelOrdering, + expError: connectiontypes.ErrInvalidConnection, // the explicit checks on the host connection identifier are unreachable + }, + { + name: "failure: channel not found", + malleate: func() { + path.EndpointB.ChannelID = "invalid-channel-id" + }, + expError: channeltypes.ErrChannelNotFound, }, } diff --git a/modules/apps/27-interchain-accounts/host/keeper/keeper.go b/modules/apps/27-interchain-accounts/host/keeper/keeper.go index be0cfc8a0b5..92055909a00 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper.go @@ -16,6 +16,7 @@ import ( genesistypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/genesis/types" "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" @@ -84,6 +85,15 @@ func (Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", exported.ModuleName, icatypes.ModuleName)) } +// getConnectionID returns the connection id for the given port and channelIDs. +func (k Keeper) getConnectionID(ctx sdk.Context, portID, channelID string) (string, error) { + channel, found := k.channelKeeper.GetChannel(ctx, portID, channelID) + if !found { + return "", errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + return channel.ConnectionHops[0], nil +} + // setPort sets the provided portID in state. func (k Keeper) setPort(ctx sdk.Context, portID string) { store := ctx.KVStore(k.storeKey) diff --git a/modules/apps/27-interchain-accounts/types/metadata.go b/modules/apps/27-interchain-accounts/types/metadata.go index 61c077a46f0..a63e4a93083 100644 --- a/modules/apps/27-interchain-accounts/types/metadata.go +++ b/modules/apps/27-interchain-accounts/types/metadata.go @@ -78,7 +78,8 @@ func IsPreviousMetadataEqual(previousVersion string, metadata Metadata) bool { previousMetadata.TxType == metadata.TxType) } -// ValidateControllerMetadata performs validation of the provided ICS27 controller metadata parameters +// ValidateControllerMetadata performs validation of the provided ICS27 controller metadata parameters as well +// as the connection params against the provided metadata func ValidateControllerMetadata(ctx sdk.Context, channelKeeper ChannelKeeper, connectionHops []string, metadata Metadata) error { if !isSupportedEncoding(metadata.Encoding) { return errorsmod.Wrapf(ErrInvalidCodec, "unsupported encoding format %s", metadata.Encoding) diff --git a/modules/apps/29-fee/ibc_middleware.go b/modules/apps/29-fee/ibc_middleware.go index 3f993d3f334..becaa10b2ca 100644 --- a/modules/apps/29-fee/ibc_middleware.go +++ b/modules/apps/29-fee/ibc_middleware.go @@ -332,27 +332,27 @@ func (im IBCMiddleware) OnChanUpgradeInit( ctx sdk.Context, portID string, channelID string, - order channeltypes.Order, - connectionHops []string, - upgradeVersion string, + proposedOrder channeltypes.Order, + proposedConnectionHops []string, + proposedVersion string, ) (string, error) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { return "", errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack") } - versionMetadata, err := types.MetadataFromVersion(upgradeVersion) + versionMetadata, err := types.MetadataFromVersion(proposedVersion) if err != nil { // since it is valid for fee version to not be specified, the upgrade version may be for a middleware // or application further down in the stack. Thus, passthrough to next middleware or application in callstack. - return cbs.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, upgradeVersion) + return cbs.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } if versionMetadata.FeeVersion != types.Version { return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) } - appVersion, err := cbs.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, versionMetadata.AppVersion) + appVersion, err := cbs.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, versionMetadata.AppVersion) if err != nil { return "", err } @@ -367,7 +367,7 @@ func (im IBCMiddleware) OnChanUpgradeInit( } // OnChanUpgradeTry implement s the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { return "", errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack") @@ -377,14 +377,14 @@ func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID stri if err != nil { // since it is valid for fee version to not be specified, the counterparty upgrade version may be for a middleware // or application further down in the stack. Thus, passthrough to next middleware or application in callstack. - return cbs.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion) + return cbs.OnChanUpgradeTry(ctx, portID, channelID, proposedOrder, proposedConnectionHops, counterpartyVersion) } if versionMetadata.FeeVersion != types.Version { return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) } - appVersion, err := cbs.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, versionMetadata.AppVersion) + appVersion, err := cbs.OnChanUpgradeTry(ctx, portID, channelID, proposedOrder, proposedConnectionHops, versionMetadata.AppVersion) if err != nil { return "", err } @@ -421,23 +421,23 @@ func (im IBCMiddleware) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, cou } // OnChanUpgradeOpen implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { panic(errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack")) } - versionMetadata, err := types.MetadataFromVersion(version) + versionMetadata, err := types.MetadataFromVersion(proposedVersion) if err != nil { // set fee disabled and passthrough to the next middleware or application in callstack. im.keeper.DeleteFeeEnabled(ctx, portID, channelID) - cbs.OnChanUpgradeOpen(ctx, portID, channelID, order, connectionHops, version) + cbs.OnChanUpgradeOpen(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) return } // set fee enabled and passthrough to the next middleware of application in callstack. im.keeper.SetFeeEnabled(ctx, portID, channelID) - cbs.OnChanUpgradeOpen(ctx, portID, channelID, order, connectionHops, versionMetadata.AppVersion) + cbs.OnChanUpgradeOpen(ctx, portID, channelID, proposedOrder, proposedConnectionHops, versionMetadata.AppVersion) } // OnChanUpgradeRestore implements the IBCModule interface diff --git a/modules/apps/callbacks/go.mod b/modules/apps/callbacks/go.mod index dbfa55bcd7d..e2a57ea621c 100644 --- a/modules/apps/callbacks/go.mod +++ b/modules/apps/callbacks/go.mod @@ -12,10 +12,10 @@ require ( cosmossdk.io/api v0.7.2 cosmossdk.io/client/v2 v2.0.0-beta.1 cosmossdk.io/core v0.11.0 - cosmossdk.io/errors v1.0.0 - cosmossdk.io/log v1.2.1 + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.0 cosmossdk.io/math v1.2.0 - cosmossdk.io/store v1.0.1 + cosmossdk.io/store v1.0.2 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/circuit v0.1.0 cosmossdk.io/x/evidence v0.1.0 @@ -187,9 +187,9 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.153.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/grpc v1.60.1 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/modules/apps/callbacks/go.sum b/modules/apps/callbacks/go.sum index 5e635b2874c..8a716140404 100644 --- a/modules/apps/callbacks/go.sum +++ b/modules/apps/callbacks/go.sum @@ -194,14 +194,14 @@ cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= -cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= -cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= -cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= -cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= -cosmossdk.io/store v1.0.1 h1:XBDhCqlL+2MUgE8CHWwndKVJ4beX+TyaPIjB5SV62dM= -cosmossdk.io/store v1.0.1/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/tools/confix v0.1.1 h1:aexyRv9+y15veH3Qw16lxQwo+ki7r2I+g0yNTEFEQM8= cosmossdk.io/tools/confix v0.1.1/go.mod h1:nQVvP1tHsGXS83PonPVWJtSbddIqyjEw99L4M3rPJyQ= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= @@ -1578,12 +1578,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 h1:kzJAXnzZoFbe5bhZd4zjUuHos/I31yH4thfMb/13oVY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/modules/apps/callbacks/ibc_middleware.go b/modules/apps/callbacks/ibc_middleware.go index 5c1b2875656..d43ab06cff6 100644 --- a/modules/apps/callbacks/ibc_middleware.go +++ b/modules/apps/callbacks/ibc_middleware.go @@ -365,23 +365,23 @@ func (im IBCMiddleware) OnChanCloseConfirm(ctx sdk.Context, portID, channelID st } // OnChanUpgradeInit implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) (string, error) { +func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) (string, error) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { return "", errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack") } - return cbs.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, version) + return cbs.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } // OnChanUpgradeTry implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { return "", errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack") } - return cbs.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion) + return cbs.OnChanUpgradeTry(ctx, portID, channelID, proposedOrder, proposedConnectionHops, counterpartyVersion) } // OnChanUpgradeAck implements the IBCModule interface @@ -395,13 +395,13 @@ func (im IBCMiddleware) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, cou } // OnChanUpgradeOpen implements the IBCModule interface -func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (im IBCMiddleware) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { cbs, ok := im.app.(porttypes.UpgradableModule) if !ok { panic(errorsmod.Wrap(porttypes.ErrInvalidRoute, "upgrade route not found to module in application callstack")) } - cbs.OnChanUpgradeOpen(ctx, portID, channelID, order, connectionHops, version) + cbs.OnChanUpgradeOpen(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } // OnChanUpgradeRestore implements the IBCModule interface diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 02ed5fc9d61..28ae28d8f65 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -197,7 +197,7 @@ func (im IBCModule) OnRecvPacket( ackErr = err logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence)) } else { - logger.Info("successfully handled ICS-20 packet sequence: %d", packet.Sequence) + logger.Info("successfully handled ICS-20 packet", "sequence", packet.Sequence) } } @@ -309,21 +309,21 @@ func (im IBCModule) OnTimeoutPacket( } // OnChanUpgradeInit implements the IBCModule interface -func (im IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, upgradeVersion string) (string, error) { - if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil { +func (im IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) (string, error) { + if err := ValidateTransferChannelParams(ctx, im.keeper, proposedOrder, portID, channelID); err != nil { return "", err } - if upgradeVersion != types.Version { - return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, upgradeVersion) + if proposedVersion != types.Version { + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, proposedVersion) } - return upgradeVersion, nil + return proposedVersion, nil } // OnChanUpgradeTry implements the IBCModule interface -func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { - if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil { +func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { + if err := ValidateTransferChannelParams(ctx, im.keeper, proposedOrder, portID, channelID); err != nil { return "", err } @@ -344,7 +344,7 @@ func (IBCModule) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, counterpar } // OnChanUpgradeOpen implements the IBCModule interface -func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { } // OnChanUpgradeRestore implements the IBCModule interface diff --git a/modules/capability/go.mod b/modules/capability/go.mod index 715021e15a5..db3f52d9ec2 100644 --- a/modules/capability/go.mod +++ b/modules/capability/go.mod @@ -6,10 +6,10 @@ replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.2021 require ( cosmossdk.io/core v0.11.0 - cosmossdk.io/errors v1.0.0 - cosmossdk.io/log v1.2.1 + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.0 cosmossdk.io/math v1.2.0 - cosmossdk.io/store v1.0.1 + cosmossdk.io/store v1.0.2 github.com/cometbft/cometbft v0.38.2 github.com/cosmos/cosmos-db v1.0.0 github.com/cosmos/cosmos-sdk v0.50.2 @@ -143,15 +143,15 @@ require ( golang.org/x/crypto v0.16.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.19.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect diff --git a/modules/capability/go.sum b/modules/capability/go.sum index cae3ad1d479..24614ff28e9 100644 --- a/modules/capability/go.sum +++ b/modules/capability/go.sum @@ -43,14 +43,14 @@ cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= -cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= -cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= -cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= -cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= -cosmossdk.io/store v1.0.1 h1:XBDhCqlL+2MUgE8CHWwndKVJ4beX+TyaPIjB5SV62dM= -cosmossdk.io/store v1.0.1/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/x/tx v0.12.0 h1:Ry2btjQdrfrje9qZ3iZeZSmDArjgxUJMMcLMrX4wj5U= cosmossdk.io/x/tx v0.12.0/go.mod h1:qTth2coAGkwCwOCjqQ8EAQg+9udXNRzcnSbMgGKGEI0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -917,8 +917,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1147,12 +1147,12 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1179,8 +1179,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1195,8 +1195,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/modules/core/02-client/types/keys.go b/modules/core/02-client/types/keys.go index 1ce683827af..8d054745aac 100644 --- a/modules/core/02-client/types/keys.go +++ b/modules/core/02-client/types/keys.go @@ -28,6 +28,10 @@ const ( // ParamsKey is the store key for the IBC client parameters ParamsKey = "clientParams" + + // AllowAllClients is the value that if set in AllowedClients param + // would allow any wired up light client modules to be allowed + AllowAllClients = "*" ) // FormatClientIdentifier returns the client identifier with the sequence appended. diff --git a/modules/core/02-client/types/params.go b/modules/core/02-client/types/params.go index a6e8241f898..da5ce32ff46 100644 --- a/modules/core/02-client/types/params.go +++ b/modules/core/02-client/types/params.go @@ -4,12 +4,11 @@ import ( "fmt" "slices" "strings" - - "github.com/cosmos/ibc-go/v8/modules/core/exported" ) // DefaultAllowedClients are the default clients for the AllowedClients parameter. -var DefaultAllowedClients = []string{exported.Solomachine, exported.Tendermint, exported.Localhost} +// By default it allows all client types. +var DefaultAllowedClients = []string{AllowAllClients} // NewParams creates a new parameter configuration for the ibc client module func NewParams(allowedClients ...string) Params { @@ -30,11 +29,27 @@ func (p Params) Validate() error { // IsAllowedClient checks if the given client type is registered on the allowlist. func (p Params) IsAllowedClient(clientType string) bool { + // Still need to check for blank client type + if strings.TrimSpace(clientType) == "" { + return false + } + + // Check for allow all client wildcard + // If exist then allow all type of client + if len(p.AllowedClients) == 1 && p.AllowedClients[0] == AllowAllClients { + return true + } + return slices.Contains(p.AllowedClients, clientType) } // validateClients checks that the given clients are not blank and there are no duplicates. +// If AllowAllClients wildcard (*) is used, then there should no other client types in the allow list func validateClients(clients []string) error { + if slices.Contains(clients, AllowAllClients) && len(clients) > 1 { + return fmt.Errorf("allow list must have only one element because the allow all clients wildcard (%s) is present", AllowAllClients) + } + foundClients := make(map[string]bool, len(clients)) for i, clientType := range clients { if strings.TrimSpace(clientType) == "" { diff --git a/modules/core/02-client/types/params_test.go b/modules/core/02-client/types/params_test.go index 2fbc674c4d1..347b913450f 100644 --- a/modules/core/02-client/types/params_test.go +++ b/modules/core/02-client/types/params_test.go @@ -19,6 +19,8 @@ func TestIsAllowedClient(t *testing.T) { {"success: valid client with custom params", exported.Tendermint, NewParams(exported.Tendermint), true}, {"success: invalid blank client", " ", DefaultParams(), false}, {"success: invalid client with custom params", exported.Localhost, NewParams(exported.Tendermint), false}, + {"success: wildcard allow all clients", "test-client-type", NewParams(AllowAllClients), true}, + {"success: wildcard allow all clients with blank client", " ", NewParams(AllowAllClients), false}, } for _, tc := range testCases { @@ -37,6 +39,7 @@ func TestValidateParams(t *testing.T) { {"custom params", NewParams(exported.Tendermint), true}, {"blank client", NewParams(" "), false}, {"duplicate clients", NewParams(exported.Tendermint, exported.Tendermint), false}, + {"allow all clients plus valid client", NewParams(AllowAllClients, exported.Tendermint), false}, } for _, tc := range testCases { diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index a4d4ea33683..765a0384a3c 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -408,43 +408,6 @@ func (k Keeper) VerifyChannelUpgradeError( return nil } -// VerifyChannelUpgradeErrorAbsence verifies a proof of the absence of a -// channel upgrade error. -func (k Keeper) VerifyChannelUpgradeErrorAbsence( - ctx sdk.Context, - connection exported.ConnectionI, - proofHeight exported.Height, - proofErrorReceiptAbsence []byte, - portID, - channelID string, -) error { - clientID := connection.GetClientID() - clientState, clientStore, err := k.getClientStateAndVerificationStore(ctx, clientID) - if err != nil { - return err - } - - if status := k.clientKeeper.GetClientStatus(ctx, clientState, clientID); status != exported.Active { - return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) - } - - merklePath := commitmenttypes.NewMerklePath(host.ChannelUpgradeErrorPath(portID, channelID)) - merklePath, err = commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) - if err != nil { - return err - } - - if err := clientState.VerifyNonMembership( - ctx, clientStore, k.cdc, proofHeight, - 0, 0, - proofErrorReceiptAbsence, merklePath, - ); err != nil { - return errorsmod.Wrapf(err, "failed upgrade error receipt absence verification for client (%s)", clientID) - } - - return nil -} - // VerifyChannelUpgrade verifies the proof that a particular proposed upgrade has been stored in the upgrade path. func (k Keeper) VerifyChannelUpgrade( ctx sdk.Context, diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index e6a01a3b4aa..8a8dd958b65 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -764,77 +764,6 @@ func (suite *KeeperTestSuite) TestVerifyUpgradeErrorReceipt() { } } -func (suite *KeeperTestSuite) TestVerifyUpgradeErrorReceiptAbsence() { - var path *ibctesting.Path - - cases := []struct { - name string - malleate func() - expPass bool - }{ - { - name: "success", - malleate: func() {}, - expPass: true, - }, - { - name: "fails when client state is frozen", - malleate: func() { - clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointB.SetClientState(clientState) - }, - expPass: false, - }, - { - name: "fails with bad client id", - malleate: func() { - connection := path.EndpointB.GetConnection() - connection.ClientId = ibctesting.InvalidID - path.EndpointB.SetConnection(connection) - }, - expPass: false, - }, - { - name: "verification fails when the key exists", - malleate: func() { - upgradeErr := channeltypes.NewUpgradeError(1, channeltypes.ErrInvalidChannel) - suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.WriteErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, upgradeErr) - suite.chainA.Coordinator.CommitBlock(suite.chainA) - suite.Require().NoError(path.EndpointB.UpdateClient()) - }, - expPass: false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - - suite.chainA.Coordinator.CommitBlock(suite.chainA) - suite.Require().NoError(path.EndpointB.UpdateClient()) - - tc.malleate() - - upgradeErrorReceiptKey := host.ChannelUpgradeErrorKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) - proof, proofHeight := suite.chainA.QueryProof(upgradeErrorReceiptKey) - - err := suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgradeErrorAbsence(suite.chainB.GetContext(), path.EndpointB.GetConnection(), proofHeight, proof, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - func (suite *KeeperTestSuite) TestVerifyUpgrade() { var ( path *ibctesting.Path diff --git a/modules/core/04-channel/client/cli/cli.go b/modules/core/04-channel/client/cli/cli.go index 450688502e1..202d63e9238 100644 --- a/modules/core/04-channel/client/cli/cli.go +++ b/modules/core/04-channel/client/cli/cli.go @@ -49,7 +49,10 @@ func NewTxCmd() *cobra.Command { RunE: client.ValidateCmd, } - txCmd.AddCommand() + txCmd.AddCommand( + newUpgradeChannelsTxCmd(), + newPruneAcknowledgementsTxCmd(), + ) return txCmd } diff --git a/modules/core/04-channel/client/cli/tx.go b/modules/core/04-channel/client/cli/tx.go new file mode 100644 index 00000000000..6d4e28977fe --- /dev/null +++ b/modules/core/04-channel/client/cli/tx.go @@ -0,0 +1,175 @@ +package cli + +import ( + "fmt" + "regexp" + "slices" + "strconv" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + + "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" +) + +const ( + flagJSON = "json" + flagPortPattern = "port-pattern" + flagExpedited = "expedited" + flagChannelIDs = "channel-ids" +) + +// newPruneAcknowledgementsTxCmd returns the command to create a new MsgPruneAcknowledgements transaction +func newPruneAcknowledgementsTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "prune-acknowledgements [port] [channel] [limit]", + Short: "Prune expired packet acknowledgements stored in IBC state", + Long: `Prune expired packet acknowledgements and receipts stored in IBC state. Packet ackwnowledgements and + receipts are considered expired if a channel has been upgraded.`, + Example: fmt.Sprintf("%s tx %s %s prune-acknowledgements transfer channel-0 1000", version.AppName, ibcexported.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + portID, channelID := args[0], args[1] + limit, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + signer := clientCtx.GetFromAddress().String() + msg := types.NewMsgPruneAcknowledgements(portID, channelID, limit, signer) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} + +func newUpgradeChannelsTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "upgrade-channels [version]", + Short: "Upgrade IBC channels", + Long: `Submit a governance proposal to upgrade all open channels whose port matches a specified pattern +(the default is transfer), optionally, an exact list of comma separated channel IDs may be specified.`, + Args: cobra.ExactArgs(1), + Example: fmt.Sprintf(`%s tx %s %s upgrade-channels "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}" --deposit 10stake`, version.AppName, ibcexported.ModuleName, types.SubModuleName), + RunE: func(cmd *cobra.Command, args []string) error { + versionStr := args[0] + + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + portPattern, err := cmd.Flags().GetString(flagPortPattern) + if err != nil { + return err + } + + commaSeparatedChannelIDs, err := cmd.Flags().GetString(flagChannelIDs) + if err != nil { + return err + } + + displayJSON, err := cmd.Flags().GetBool(flagJSON) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + resp, err := queryClient.Channels(cmd.Context(), &types.QueryChannelsRequest{}) + if err != nil { + return err + } + + channelIDs := getChannelIDs(commaSeparatedChannelIDs) + + var msgs []sdk.Msg + pattern := regexp.MustCompile(portPattern) + + for _, ch := range resp.Channels { + + if !channelShouldBeUpgraded(*ch, pattern, channelIDs) { + continue + } + + // construct a MsgChannelUpgradeInit which will upgrade the specified channel to a specific version. + msgUpgradeInit := types.NewMsgChannelUpgradeInit(ch.PortId, ch.ChannelId, types.NewUpgradeFields(ch.Ordering, ch.ConnectionHops, versionStr), clientCtx.GetFromAddress().String()) + msgs = append(msgs, msgUpgradeInit) + } + + if len(msgs) == 0 { + return fmt.Errorf("no channels would be upgraded with pattern %s", portPattern) + } + + msgSubmitProposal, err := govcli.ReadGovPropFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } + + if err := msgSubmitProposal.SetMsgs(msgs); err != nil { + return err + } + + dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun) + if displayJSON || dryRun { + out, err := clientCtx.Codec.MarshalJSON(msgSubmitProposal) + if err != nil { + return err + } + return clientCtx.PrintBytes(out) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgSubmitProposal) + }, + } + + flags.AddTxFlagsToCmd(cmd) + govcli.AddGovPropFlagsToCmd(cmd) + cmd.Flags().Bool(flagJSON, false, "specify true to output valid proposal.json contents, instead of submitting a governance proposal.") + cmd.Flags().String(flagPortPattern, "transfer", "The pattern to use to match port ids.") + cmd.Flags().String(flagChannelIDs, "", "a comma separated list of channel IDs to upgrade.") + cmd.Flags().Bool(flagExpedited, false, "set the expedited value for the governance proposal.") + + return cmd +} + +// getChannelIDs returns a slice of channel IDs based on a comma separated string of channel IDs. +func getChannelIDs(commaSeparatedList string) []string { + if strings.TrimSpace(commaSeparatedList) == "" { + return nil + } + return strings.Split(commaSeparatedList, ",") +} + +// channelShouldBeUpgraded returns a boolean indicating whether or not the given channel should be upgraded based +// on either the provided regex pattern or list of desired channel IDs. +func channelShouldBeUpgraded(channel types.IdentifiedChannel, pattern *regexp.Regexp, channelIDs []string) bool { + // skip any channel that is not open + if channel.State != types.OPEN { + return false + } + + // if specified, the channel ID must exactly match. + if len(channelIDs) > 0 { + return pattern.MatchString(channel.PortId) && slices.Contains(channelIDs, channel.ChannelId) + } + + // otherwise we only need the port pattern to match. + return pattern.MatchString(channel.PortId) +} diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 78b7c464b3a..f8b23d35f0a 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -4,7 +4,6 @@ import ( "bytes" "slices" "strconv" - "time" errorsmod "cosmossdk.io/errors" @@ -74,25 +73,16 @@ func (k Keeper) SendPacket( return 0, errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) } - // check if packet is timed out on the receiving chain latestHeight := clientState.GetLatestHeight() - if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) { - return 0, errorsmod.Wrapf( - types.ErrPacketTimeout, - "receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight, - ) - } - latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) if err != nil { return 0, err } - if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { - return 0, errorsmod.Wrapf( - types.ErrPacketTimeout, - "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)).UTC(), time.Unix(0, int64(packet.GetTimeoutTimestamp())).UTC(), - ) + // check if packet is timed out on the receiving chain + timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp()) + if timeout.Elapsed(latestHeight.(clienttypes.Height), latestTimestamp) { + return 0, errorsmod.Wrap(timeout.ErrTimeoutElapsed(latestHeight.(clienttypes.Height), latestTimestamp), "invalid packet timeout") } commitment := types.CommitPacket(k.cdc, packet) @@ -189,22 +179,11 @@ func (k Keeper) RecvPacket( ) } - // check if packet timeouted by comparing it with the latest height of the chain - selfHeight := clienttypes.GetSelfHeight(ctx) - timeoutHeight := packet.GetTimeoutHeight() - if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) { - return errorsmod.Wrapf( - types.ErrPacketTimeout, - "block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight, - ) - } - - // check if packet timeouted by comparing it with the latest timestamp of the chain - if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() { - return errorsmod.Wrapf( - types.ErrPacketTimeout, - "block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())).UTC(), - ) + // check if packet timed out by comparing it with the latest height of the chain + selfHeight, selfTimestamp := clienttypes.GetSelfHeight(ctx), uint64(ctx.BlockTime().UnixNano()) + timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp()) + if timeout.Elapsed(selfHeight, selfTimestamp) { + return errorsmod.Wrap(timeout.ErrTimeoutElapsed(selfHeight, selfTimestamp), "packet timeout elapsed") } commitment := types.CommitPacket(k.cdc, packet) diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 8a33cb563e7..59ea2efaefe 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -562,7 +562,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, - types.ErrPacketTimeout, + types.ErrTimeoutElapsed, }, { "timeout timestamp passed", @@ -572,7 +572,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, disabledTimeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, - types.ErrPacketTimeout, + types.ErrTimeoutElapsed, }, { "next receive sequence is not found", diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index a0f7f189fa6..5e7e0a07450 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -68,10 +68,9 @@ func (k Keeper) TimeoutPacket( return err } - timeoutHeight := packet.GetTimeoutHeight() - if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) && - (packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) { - return errorsmod.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp") + timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp()) + if !timeout.Elapsed(proofHeight.(clienttypes.Height), proofTimestamp) { + return errorsmod.Wrap(timeout.ErrTimeoutNotReached(proofHeight.(clienttypes.Height), proofTimestamp), "packet timeout not reached") } commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) @@ -178,7 +177,7 @@ func (k Keeper) TimeoutExecuted( // the upgrade is aborted and the channel is set to CLOSED. if channel.State == types.FLUSHING { // an error receipt is written to state and the channel is restored to OPEN - k.MustAbortUpgrade(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), types.ErrPacketTimeout) + k.MustAbortUpgrade(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), errorsmod.Wrap(types.ErrTimeoutElapsed, "packet timeout elapsed on ORDERED channel")) } channel.State = types.CLOSED diff --git a/modules/core/04-channel/keeper/timeout_test.go b/modules/core/04-channel/keeper/timeout_test.go index 27f6def9f85..22db7d65e2f 100644 --- a/modules/core/04-channel/keeper/timeout_test.go +++ b/modules/core/04-channel/keeper/timeout_test.go @@ -124,7 +124,7 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, defaultTimeoutHeight, disabledTimeoutTimestamp) }, false}, {"timeout", func() { - expError = types.ErrPacketTimeout + expError = types.ErrTimeoutNotReached suite.coordinator.Setup(path) sequence, err := path.EndpointA.SendPacket(defaultTimeoutHeight, disabledTimeoutTimestamp, ibctesting.MockPacketData) suite.Require().NoError(err) @@ -192,9 +192,9 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { }, false}, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.Run(tc.msg, func() { var ( proof []byte proofHeight exported.Height diff --git a/modules/core/04-channel/keeper/upgrade.go b/modules/core/04-channel/keeper/upgrade.go index beb93b76480..b64f75fa540 100644 --- a/modules/core/04-channel/keeper/upgrade.go +++ b/modules/core/04-channel/keeper/upgrade.go @@ -669,18 +669,12 @@ func (k Keeper) WriteUpgradeCancelChannel(ctx sdk.Context, portID, channelID str panic(fmt.Errorf("could not find existing channel when updating channel state, channelID: %s, portID: %s", channelID, portID)) } - upgrade, found := k.GetUpgrade(ctx, portID, channelID) - if !found { - panic(fmt.Errorf("could not find upgrade when updating channel state, channelID: %s, portID: %s", channelID, portID)) - } - previousState := channel.State channel = k.restoreChannel(ctx, portID, channelID, sequence, channel) k.WriteErrorReceipt(ctx, portID, channelID, types.NewUpgradeError(sequence, types.ErrInvalidUpgrade)) k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", previousState, "new-state", types.OPEN.String()) - EmitChannelUpgradeCancelEvent(ctx, portID, channelID, channel, upgrade) } // ChanUpgradeTimeout times out an outstanding upgrade. @@ -728,11 +722,8 @@ func (k Keeper) ChanUpgradeTimeout( // proof must be from a height after timeout has elapsed. Either timeoutHeight or timeoutTimestamp must be defined. // if timeoutHeight is defined and proof is from before timeout height, abort transaction - timeoutHeight := upgrade.Timeout.Height - timeoutTimeStamp := upgrade.Timeout.Timestamp - if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) && - (timeoutTimeStamp == 0 || proofTimestamp < timeoutTimeStamp) { - return errorsmod.Wrap(types.ErrInvalidUpgradeTimeout, "upgrade timeout has not been reached for height or timestamp") + if !upgrade.Timeout.Elapsed(proofHeight.(clienttypes.Height), proofTimestamp) { + return errorsmod.Wrap(upgrade.Timeout.ErrTimeoutNotReached(proofHeight.(clienttypes.Height), proofTimestamp), "upgrade timeout not reached") } // counterparty channel must be proved to still be in OPEN state or FLUSHING state. diff --git a/modules/core/04-channel/keeper/upgrade_test.go b/modules/core/04-channel/keeper/upgrade_test.go index 463d2cb569d..690fe77b22f 100644 --- a/modules/core/04-channel/keeper/upgrade_test.go +++ b/modules/core/04-channel/keeper/upgrade_test.go @@ -110,28 +110,10 @@ func (suite *KeeperTestSuite) TestChanUpgradeInit() { suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.WriteUpgradeInitChannel(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, upgrade, upgrade.Fields.Version) channel := path.EndpointA.GetChannel() - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeInit: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyUpgradeConnectionHops: upgradeFields.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: upgradeFields.Version, - // types.AttributeKeyUpgradeOrdering: upgradeFields.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - suite.Require().NoError(err) suite.Require().Equal(expSequence, channel.UpgradeSequence) suite.Require().Equal(mock.Version, channel.Version) suite.Require().Equal(types.OPEN, channel.State) - } else { suite.Require().Error(err) } @@ -627,23 +609,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeTry() { upgrade, found := suite.chainB.GetSimApp().IBCKeeper.ChannelKeeper.GetUpgrade(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) suite.Require().True(found) suite.Require().Equal(upgradeWithAppCallbackVersion, upgrade) - - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeTry: { - // types.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointB.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, - // types.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: upgrade.Fields.Version, - // types.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } }) } } @@ -889,23 +854,6 @@ func (suite *KeeperTestSuite) TestWriteChannelUpgradeAck() { upgrade := path.EndpointA.GetChannelUpgrade() suite.Require().Equal(mock.UpgradeVersion, upgrade.Fields.Version) - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeAck: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: upgrade.Fields.Version, - // types.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - if !tc.hasPacketCommitments { suite.Require().Equal(types.FLUSHCOMPLETE, channel.State) } @@ -1178,21 +1126,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeConfirm() { upgrade := path.EndpointA.GetChannelUpgrade() suite.Require().Equal(mock.UpgradeVersion, upgrade.Fields.Version) - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeConfirm: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeKeyChannelState: channel.State.String(), - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - if !tc.hasPacketCommitments { suite.Require().Equal(types.FLUSHCOMPLETE, channel.State) } else { @@ -1432,25 +1365,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeOpenChannel() { counterpartyUpgrade, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetCounterpartyUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().Equal(types.Upgrade{}, counterpartyUpgrade) suite.Require().False(found) - - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeOpen: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyChannelState: types.OPEN.String(), - // types.AttributeKeyUpgradeConnectionHops: channel.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: channel.Version, - // types.AttributeKeyUpgradeOrdering: channel.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - // ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) } }) } @@ -1514,6 +1428,7 @@ func (suite *KeeperTestSuite) TestWriteUpgradeOpenChannel_Ordering() { suite.Require().Equal(uint64(1), seq) // Assert that pruning sequence start has been initialized (set to 1) + suite.Require().True(suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.HasPruningSequenceStart(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) pruningSeq, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetPruningSequenceStart(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found) suite.Require().Equal(uint64(1), pruningSeq) @@ -1574,6 +1489,7 @@ func (suite *KeeperTestSuite) TestWriteUpgradeOpenChannel_Ordering() { suite.Require().Equal(uint64(2), seq) // Assert that pruning sequence start has been initialized (set to 1) + suite.Require().True(suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.HasPruningSequenceStart(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) pruningSeq, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetPruningSequenceStart(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found) suite.Require().Equal(uint64(1), pruningSeq) @@ -1640,25 +1556,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeOpenChannel_Ordering() { counterpartyUpgrade, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetCounterpartyUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().Equal(types.Upgrade{}, counterpartyUpgrade) suite.Require().False(found) - - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeOpen: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyChannelState: types.OPEN.String(), - // types.AttributeKeyUpgradeConnectionHops: channel.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: channel.Version, - // types.AttributeKeyUpgradeOrdering: channel.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - // ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }) } } @@ -1945,13 +1842,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeCancelChannel() { }, expPanic: true, }, - { - name: "upgrade not found", - malleate: func() { - path.EndpointA.Chain.DeleteKey(host.ChannelUpgradeKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) - }, - expPanic: true, - }, } for _, tc := range testCases { @@ -2012,26 +1902,6 @@ func (suite *KeeperTestSuite) TestWriteUpgradeCancelChannel() { counterpartyUpgrade, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetCounterpartyUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().Equal(types.Upgrade{}, counterpartyUpgrade) suite.Require().False(found) - - // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. - // proposedUpgrade := path.EndpointA.GetProposedUpgrade() - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // types.EventTypeChannelUpgradeCancel: { - // types.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, - // types.AttributeKeyChannelID: path.EndpointA.ChannelID, - // types.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, - // types.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, - // types.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], - // types.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, - // types.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), - // types.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), - // }, - // sdk.EventTypeMessage: { - // sdk.AttributeKeyModule: types.AttributeValueCategory, - // }, - // } - } }) } @@ -2162,7 +2032,7 @@ func (suite *KeeperTestSuite) TestChanUpgradeTimeout() { channelKey := host.ChannelKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) proofChannel, proofHeight = path.EndpointB.QueryProof(channelKey) }, - types.ErrInvalidUpgradeTimeout, + types.ErrTimeoutNotReached, }, { "counterparty channel state is not OPEN or FLUSHING (crossing hellos)", @@ -2541,18 +2411,6 @@ func (suite *KeeperTestSuite) TestAbortUpgrade() { channelKeeper.MustAbortUpgrade(ctx, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, upgradeError) }) - // events := ctx.EventManager().Events().ToABCIEvents() - // expEvents := ibctesting.EventsMap{ - // "channel_upgrade_error": { - // "port_id": path.EndpointA.ChannelConfig.PortID, - // "channel_id": path.EndpointA.ChannelID, - // "counterparty_port_id": path.EndpointB.ChannelConfig.PortID, - // "counterparty_channel_id": path.EndpointB.ChannelID, - // "upgrade_sequence": fmt.Sprintf("%d", path.EndpointA.GetChannel().UpgradeSequence), - // "upgrade_error_receipt": upgradeError.Error(), - // }, - // } - channel, found := channelKeeper.GetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found, "channel should be found") diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index 9b3b4b2e2de..d46c514182e 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -91,14 +91,6 @@ type ConnectionKeeper interface { channelID string, errorReceipt ErrorReceipt, ) error - VerifyChannelUpgradeErrorAbsence( - ctx sdk.Context, - connection exported.ConnectionI, - proofHeight exported.Height, - proofErrorReceiptAbsence []byte, - portID, - channelID string, - ) error } // PortKeeper expected account IBC port keeper diff --git a/modules/core/04-channel/types/msgs.go b/modules/core/04-channel/types/msgs.go index c1d708b480e..24f4fcaecd1 100644 --- a/modules/core/04-channel/types/msgs.go +++ b/modules/core/04-channel/types/msgs.go @@ -25,6 +25,9 @@ var ( _ sdk.Msg = (*MsgAcknowledgement)(nil) _ sdk.Msg = (*MsgTimeout)(nil) _ sdk.Msg = (*MsgTimeoutOnClose)(nil) + _ sdk.Msg = (*MsgChannelUpgradeInit)(nil) + _ sdk.Msg = (*MsgChannelUpgradeTry)(nil) + _ sdk.Msg = (*MsgChannelUpgradeAck)(nil) _ sdk.Msg = (*MsgChannelUpgradeConfirm)(nil) _ sdk.Msg = (*MsgChannelUpgradeTimeout)(nil) _ sdk.Msg = (*MsgChannelUpgradeCancel)(nil) diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index 23dba849307..77b6ae6fad7 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -116,9 +116,9 @@ type UpgradableModule interface { OnChanUpgradeInit( ctx sdk.Context, portID, channelID string, - order channeltypes.Order, - connectionHops []string, - version string, + proposedOrder channeltypes.Order, + proposedConnectionHops []string, + proposedVersion string, ) (string, error) // OnChanUpgradeTry enables additional custom logic to be executed in the ChannelUpgradeTry step of the @@ -127,8 +127,8 @@ type UpgradableModule interface { OnChanUpgradeTry( ctx sdk.Context, portID, channelID string, - order channeltypes.Order, - connectionHops []string, + proposedOrder channeltypes.Order, + proposedConnectionHops []string, counterpartyVersion string, ) (string, error) @@ -148,9 +148,9 @@ type UpgradableModule interface { ctx sdk.Context, portID, channelID string, - order channeltypes.Order, - connectionHops []string, - version string, + proposedOrder channeltypes.Order, + proposedConnectionHops []string, + proposedVersion string, ) // OnChanUpgradeRestore enables additional custom logic to be executed when any of the following occur: diff --git a/modules/core/keeper/events_test.go b/modules/core/keeper/events_test.go new file mode 100644 index 00000000000..d7912dc6944 --- /dev/null +++ b/modules/core/keeper/events_test.go @@ -0,0 +1,106 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v8/modules/core/keeper" + "github.com/cosmos/ibc-go/v8/modules/core/types" +) + +func TestConvertToErrorEvents(t *testing.T) { + var ( + events sdk.Events + expEvents sdk.Events + ) + + tc := []struct { + name string + malleate func() + }{ + { + "success: nil events", + func() { + events = nil + expEvents = nil + }, + }, + { + "success: empty events", + func() { + events = sdk.Events{} + expEvents = sdk.Events{} + }, + }, + { + "success: event with no attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent"), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix + "testevent"), + } + }, + }, + { + "success: event with attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent", + sdk.NewAttribute("key1", "value1"), + sdk.NewAttribute("key2", "value2"), + ), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key1", "value1"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key2", "value2"), + ), + } + }, + }, + { + "success: multiple events with attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent1", + sdk.NewAttribute("key1", "value1"), + sdk.NewAttribute("key2", "value2"), + ), + sdk.NewEvent("testevent2", + sdk.NewAttribute("key3", "value3"), + sdk.NewAttribute("key4", "value4"), + ), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent1", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key1", "value1"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key2", "value2"), + ), + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent2", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key3", "value3"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key4", "value4"), + ), + } + }, + }, + } + + for _, tc := range tc { + t.Run(tc.name, func(t *testing.T) { + // initial events and expected events are reset so that the test fails if + // the malleate function does not set them + events = nil + expEvents = sdk.Events{} + + tc.malleate() + + newEvents := keeper.ConvertToErrorEvents(events) + require.Equal(t, expEvents, newEvents) + }) + } +} diff --git a/modules/core/keeper/export_test.go b/modules/core/keeper/export_test.go new file mode 100644 index 00000000000..81d79ea7aa9 --- /dev/null +++ b/modules/core/keeper/export_test.go @@ -0,0 +1,9 @@ +package keeper + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// ConvertToErrorEvents is a wrapper around convertToErrorEvents +// to allow the function to be directly called in tests. +func ConvertToErrorEvents(events sdk.Events) sdk.Events { + return convertToErrorEvents(events) +} diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index ee3bfe30f5f..d71b68952d4 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -494,6 +494,9 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke if ack == nil || ack.Success() { // write application state changes for asynchronous and successful acknowledgements writeFn() + } else { + // Modify events in cached context to reflect unsuccessful acknowledgement + ctx.EventManager().EmitEvents(convertToErrorEvents(cacheCtx.EventManager().Events())) } // Set packet acknowledgement only if the acknowledgement is not nil. @@ -856,19 +859,22 @@ func (k Keeper) ChannelUpgradeAck(goCtx context.Context, msg *channeltypes.MsgCh app, ok := k.Router.GetRoute(module) if !ok { - ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", err) + return nil, err } cbs, ok := app.(porttypes.UpgradableModule) if !ok { - ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", err) + return nil, err } err = k.ChannelKeeper.ChanUpgradeAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyUpgrade, msg.ProofChannel, msg.ProofUpgrade, msg.ProofHeight) if err != nil { - ctx.Logger().Error("channel upgrade ack failed", "error", errorsmod.Wrap(err, "channel upgrade ack failed")) + errChanUpgradeFailed := errorsmod.Wrap(err, "channel upgrade ack failed") + ctx.Logger().Error("channel upgrade ack failed", "error", errChanUpgradeFailed) if channeltypes.IsUpgradeError(err) { k.ChannelKeeper.MustAbortUpgrade(ctx, msg.PortId, msg.ChannelId, err) cbs.OnChanUpgradeRestore(ctx, msg.PortId, msg.ChannelId) @@ -879,7 +885,7 @@ func (k Keeper) ChannelUpgradeAck(goCtx context.Context, msg *channeltypes.MsgCh } // NOTE: an error is returned to baseapp and transaction state is not committed. - return nil, errorsmod.Wrap(err, "channel upgrade ack failed") + return nil, errChanUpgradeFailed } cacheCtx, writeFn := ctx.CacheContext() @@ -914,14 +920,16 @@ func (k Keeper) ChannelUpgradeConfirm(goCtx context.Context, msg *channeltypes.M app, ok := k.Router.GetRoute(module) if !ok { - ctx.Logger().Error("channel upgrade confirm failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel upgrade confirm failed", "port-id", msg.PortId, "error", err) + return nil, err } cbs, ok := app.(porttypes.UpgradableModule) if !ok { - ctx.Logger().Error("channel upgrade confirm failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + ctx.Logger().Error("channel upgrade confirm failed", "port-id", msg.PortId, "error", err) + return nil, err } err = k.ChannelKeeper.ChanUpgradeConfirm(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannelState, msg.CounterpartyUpgrade, msg.ProofChannel, msg.ProofUpgrade, msg.ProofHeight) @@ -952,7 +960,7 @@ func (k Keeper) ChannelUpgradeConfirm(goCtx context.Context, msg *channeltypes.M return nil, errorsmod.Wrapf(channeltypes.ErrUpgradeNotFound, "failed to retrieve channel upgrade: port ID (%s) channel ID (%s)", msg.PortId, msg.ChannelId) } - k.ChannelKeeper.WriteUpgradeOpenChannel(ctx, msg.PortId, msg.ChannelId) + channel := k.ChannelKeeper.WriteUpgradeOpenChannel(ctx, msg.PortId, msg.ChannelId) cbs.OnChanUpgradeOpen(ctx, msg.PortId, msg.ChannelId, upgrade.Fields.Ordering, upgrade.Fields.ConnectionHops, upgrade.Fields.Version) ctx.Logger().Info("channel upgrade open succeeded", "port-id", msg.PortId, "channel-id", msg.ChannelId) @@ -974,14 +982,16 @@ func (k Keeper) ChannelUpgradeOpen(goCtx context.Context, msg *channeltypes.MsgC app, ok := k.Router.GetRoute(module) if !ok { - ctx.Logger().Error("channel upgrade open failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel upgrade open failed", "port-id", msg.PortId, "error", err) + return nil, err } cbs, ok := app.(porttypes.UpgradableModule) if !ok { - ctx.Logger().Error("channel upgrade open failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + ctx.Logger().Error("channel upgrade open failed", "port-id", msg.PortId, "error", err) + return nil, err } if err = k.ChannelKeeper.ChanUpgradeOpen(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannelState, msg.ProofChannel, msg.ProofHeight); err != nil { @@ -1015,14 +1025,16 @@ func (k Keeper) ChannelUpgradeTimeout(goCtx context.Context, msg *channeltypes.M app, ok := k.Router.GetRoute(module) if !ok { - ctx.Logger().Error("channel upgrade timeout failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel upgrade timeout failed", "port-id", msg.PortId, "error", err) + return nil, err } cbs, ok := app.(porttypes.UpgradableModule) if !ok { - ctx.Logger().Error("channel upgrade timeout failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + ctx.Logger().Error("channel upgrade timeout failed", "port-id", msg.PortId, "error", err) + return nil, err } err = k.ChannelKeeper.ChanUpgradeTimeout(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannel, msg.ProofChannel, msg.ProofHeight) @@ -1051,14 +1063,16 @@ func (k Keeper) ChannelUpgradeCancel(goCtx context.Context, msg *channeltypes.Ms app, ok := k.Router.GetRoute(module) if !ok { - ctx.Logger().Error("channel upgrade cancel failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + ctx.Logger().Error("channel upgrade cancel failed", "port-id", msg.PortId, "error", err) + return nil, err } cbs, ok := app.(porttypes.UpgradableModule) if !ok { - ctx.Logger().Error("channel upgrade cancel failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module)) - return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + err = errorsmod.Wrapf(porttypes.ErrInvalidRoute, "upgrade route not found to module: %s", module) + ctx.Logger().Error("channel upgrade cancel failed", "port-id", msg.PortId, err) + return nil, err } channel, found := k.ChannelKeeper.GetChannel(ctx, msg.PortId, msg.ChannelId) @@ -1070,12 +1084,19 @@ func (k Keeper) ChannelUpgradeCancel(goCtx context.Context, msg *channeltypes.Ms // then we can restore immediately without any additional checks isAuthority := k.GetAuthority() == msg.Signer if isAuthority && channel.State != channeltypes.FLUSHCOMPLETE { + upgrade, found := k.ChannelKeeper.GetUpgrade(ctx, msg.PortId, msg.ChannelId) + if !found { + return nil, errorsmod.Wrapf(channeltypes.ErrUpgradeNotFound, "failed to retrieve channel upgrade: port ID (%s) channel ID (%s)", msg.PortId, msg.ChannelId) + } + k.ChannelKeeper.WriteUpgradeCancelChannel(ctx, msg.PortId, msg.ChannelId, channel.UpgradeSequence) cbs.OnChanUpgradeRestore(ctx, msg.PortId, msg.ChannelId) ctx.Logger().Info("channel upgrade cancel succeeded", "port-id", msg.PortId, "channel-id", msg.ChannelId) + keeper.EmitChannelUpgradeCancelEvent(ctx, msg.PortId, msg.ChannelId, channel, upgrade) + return &channeltypes.MsgChannelUpgradeCancelResponse{}, nil } @@ -1084,12 +1105,25 @@ func (k Keeper) ChannelUpgradeCancel(goCtx context.Context, msg *channeltypes.Ms return nil, errorsmod.Wrap(err, "channel upgrade cancel failed") } + // get upgrade here since it will be deleted in WriteUpgradeCancelChannel + upgrade, found := k.ChannelKeeper.GetUpgrade(ctx, msg.PortId, msg.ChannelId) + if !found { + return nil, errorsmod.Wrapf(channeltypes.ErrUpgradeNotFound, "failed to retrieve channel upgrade: port ID (%s) channel ID (%s)", msg.PortId, msg.ChannelId) + } + k.ChannelKeeper.WriteUpgradeCancelChannel(ctx, msg.PortId, msg.ChannelId, msg.ErrorReceipt.Sequence) cbs.OnChanUpgradeRestore(ctx, msg.PortId, msg.ChannelId) ctx.Logger().Info("channel upgrade cancel succeeded", "port-id", msg.PortId, "channel-id", msg.ChannelId) + // get channel here again to get latest state after write + channel, found = k.ChannelKeeper.GetChannel(ctx, msg.PortId, msg.ChannelId) + if !found { + return nil, errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", msg.PortId, msg.ChannelId) + } + keeper.EmitChannelUpgradeCancelEvent(ctx, msg.PortId, msg.ChannelId, channel, upgrade) + return &channeltypes.MsgChannelUpgradeCancelResponse{}, nil } @@ -1143,3 +1177,23 @@ func (k Keeper) UpdateChannelParams(goCtx context.Context, msg *channeltypes.Msg return &channeltypes.MsgUpdateParamsResponse{}, nil } + +// convertToErrorEvents converts all events to error events by appending the +// error attribute prefix to each event's attribute key. +func convertToErrorEvents(events sdk.Events) sdk.Events { + if events == nil { + return nil + } + + newEvents := make(sdk.Events, len(events)) + for i, event := range events { + newAttributes := make([]sdk.Attribute, len(event.Attributes)) + for j, attribute := range event.Attributes { + newAttributes[j] = sdk.NewAttribute(coretypes.ErrorAttributeKeyPrefix+attribute.Key, attribute.Value) + } + + newEvents[i] = sdk.NewEvent(coretypes.ErrorAttributeKeyPrefix+event.Type, newAttributes...) + } + + return newEvents +} diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index 25b0fb9729f..5b647181fe8 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -9,10 +9,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + abci "github.com/cometbft/cometbft/abci/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" @@ -37,7 +40,6 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { var ( packet channeltypes.Packet path *ibctesting.Path - async bool // indicate no ack written ) testCases := []struct { @@ -45,6 +47,8 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { malleate func() expPass bool expRevert bool + async bool // indicate no ack written + replay bool // indicate replay (no-op) }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -54,7 +58,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, false, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -62,7 +66,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, false, false}, {"success: UNORDERED out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -74,7 +78,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - }, true, false}, + }, true, false, false, false}, {"success: OnRecvPacket callback returns revert=true", func() { suite.coordinator.Setup(path) @@ -82,26 +86,24 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockFailPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, true}, + }, true, true, false, false}, {"success: ORDERED - async acknowledgement", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - async = true sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, true, false}, {"success: UNORDERED - async acknowledgement", func() { suite.coordinator.Setup(path) - async = true sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, true, false}, {"failure: ORDERED out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -113,15 +115,15 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - }, false, false}, + }, false, false, false, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) - }, false, false}, + }, false, false, false, false}, {"packet not sent", func() { suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, false, false}, + }, false, false, false, false}, {"successful no-op: ORDERED - packet already received (replay)", func() { // mock will panic if application callback is called twice on the same packet path.SetChannelOrdered() @@ -133,7 +135,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true, false}, + }, true, false, false, true}, {"successful no-op: UNORDERED - packet already received (replay)", func() { // mock will panic if application callback is called twice on the same packet suite.coordinator.Setup(path) @@ -144,7 +146,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true, false}, + }, true, false, false, true}, } for _, tc := range testCases { @@ -152,7 +154,6 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Run(tc.name, func() { suite.SetupTest() // reset - async = false // reset path = ibctesting.NewPath(suite.chainA, suite.chainB) tc.malleate() @@ -170,7 +171,10 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { msg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.GetIBCKeeper(), suite.chainB.GetContext(), msg) + ctx := suite.chainB.GetContext() + _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.GetIBCKeeper(), ctx, msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -183,14 +187,27 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { _, exists := suite.chainB.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainB.GetContext(), ibcmock.GetMockRecvCanaryCapabilityName(packet)) if tc.expRevert { suite.Require().False(exists, "capability exists in store even after callback reverted") + + // context events should contain error events + suite.Require().Contains(events, keeper.ConvertToErrorEvents(sdk.Events{ibcmock.NewMockRecvPacketEvent()})[0]) + suite.Require().NotContains(events, ibcmock.NewMockRecvPacketEvent()) } else { suite.Require().True(exists, "callback state not persisted when revert is false") + + if tc.replay { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockRecvPacketEvent()) + suite.Require().NotContains(events, keeper.ConvertToErrorEvents(sdk.Events{ibcmock.NewMockRecvPacketEvent()})[0]) + } else { + // context events should contain application events + suite.Require().Contains(events, ibcmock.NewMockRecvPacketEvent()) + } } // verify if ack was written ack, found := suite.chainB.App.GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - if async { + if tc.async { suite.Require().Nil(ack) suite.Require().False(found) @@ -296,6 +313,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { name string malleate func() expPass bool + replay bool // indicate replay (no-op) }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -307,7 +325,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true}, + }, true, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -317,7 +335,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true}, + }, true, false}, {"success: UNORDERED acknowledge out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -331,7 +349,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } - }, true}, + }, true, false}, {"failure: ORDERED acknowledge out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -345,11 +363,11 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } - }, false}, + }, false, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) - }, false}, + }, false, false}, {"packet not received", func() { suite.coordinator.Setup(path) @@ -357,7 +375,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, false}, + }, false, false}, {"successful no-op: ORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) @@ -370,7 +388,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, true}, + }, true, true}, {"successful no-op: UNORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) @@ -383,7 +401,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, true}, + }, true, true}, } for _, tc := range testCases { @@ -406,7 +424,10 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { msg := channeltypes.NewMsgAcknowledgement(packet, ibcmock.MockAcknowledgement.Acknowledgement(), proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), suite.chainA.GetContext(), msg) + ctx := suite.chainA.GetContext() + _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), ctx, msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -418,6 +439,14 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { // replay should not error as it is treated as a no-op _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), suite.chainA.GetContext(), msg) suite.Require().NoError(err) + + if tc.replay { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockAckPacketEvent()) + } else { + // context events should contain application events + suite.Require().Contains(events, ibcmock.NewMockAckPacketEvent()) + } } else { suite.Require().Error(err) } @@ -441,6 +470,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { name string malleate func() expPass bool + noop bool // indicate no-op }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -459,7 +489,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, true}, + }, true, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -476,7 +506,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, false}, {"success: UNORDERED timeout out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -497,7 +527,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { suite.Require().NoError(err) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, false}, {"success: ORDERED timeout out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -518,18 +548,19 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { suite.Require().NoError(err) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, true}, + }, true, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, false}, + }, false, false}, {"successful no-op: UNORDERED - packet not sent", func() { suite.coordinator.Setup(path) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 1), 0) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, true}, } for _, tc := range testCases { @@ -551,7 +582,10 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { msg := channeltypes.NewMsgTimeout(packet, 1, proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.Timeout(*suite.chainA.App.GetIBCKeeper(), suite.chainA.GetContext(), msg) + ctx := suite.chainA.GetContext() + _, err := keeper.Keeper.Timeout(*suite.chainA.App.GetIBCKeeper(), ctx, msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -564,6 +598,14 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { has := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) suite.Require().False(has) + if tc.noop { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockTimeoutPacketEvent()) + } else { + // context should contain application events + suite.Require().Contains(events, ibcmock.NewMockTimeoutPacketEvent()) + } + } else { suite.Require().Error(err) } @@ -869,7 +911,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeInit() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeInitResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeInitResponse, events []abci.Event, err error) }{ { "success", @@ -881,10 +923,30 @@ func (suite *KeeperTestSuite) TestChannelUpgradeInit() { path.EndpointA.Chain.GetSimApp().IBCKeeper.GetAuthority(), ) }, - func(res *channeltypes.MsgChannelUpgradeInitResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeInitResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(uint64(1), res.UpgradeSequence) + + upgrade := path.EndpointA.GetChannelUpgrade() + channel := path.EndpointA.GetChannel() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeInit: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: upgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -897,10 +959,35 @@ func (suite *KeeperTestSuite) TestChannelUpgradeInit() { path.EndpointA.Chain.SenderAccount.String(), ) }, - func(res *channeltypes.MsgChannelUpgradeInitResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeInitResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().ErrorContains(err, ibcerrors.ErrUnauthorized.Error()) suite.Require().Nil(res) + + suite.Require().Empty(events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg = channeltypes.NewMsgChannelUpgradeInit( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + path.EndpointA.GetProposedUpgrade().Fields, + path.EndpointA.Chain.GetSimApp().IBCKeeper.GetAuthority(), + ) + }, + func(res *channeltypes.MsgChannelUpgradeInitResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -919,9 +1006,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeInit() { tc.malleate() - res, err := keeper.Keeper.ChannelUpgradeInit(*suite.chainA.App.GetIBCKeeper(), suite.chainA.GetContext(), msg) + ctx := suite.chainA.GetContext() + res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeInit(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } @@ -935,12 +1024,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeTryResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeTryResponse, events []abci.Event, err error) }{ { "success", func() {}, - func(res *channeltypes.MsgChannelUpgradeTryResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeTryResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -948,6 +1037,25 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { channel := path.EndpointB.GetChannel() suite.Require().Equal(channeltypes.FLUSHING, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + upgrade := path.EndpointB.GetChannelUpgrade() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeTry: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: upgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -956,11 +1064,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { msg.PortId = "invalid-port" msg.ChannelId = "invalid-channel" }, - func(res *channeltypes.MsgChannelUpgradeTryResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeTryResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) + suite.Require().Empty(events) }, }, { @@ -971,7 +1080,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { path.EndpointB.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeTryResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeTryResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -980,6 +1089,43 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { errorReceipt, found := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) suite.Require().True(found) suite.Require().Equal(uint64(99), errorReceipt.Sequence) + + channel := path.EndpointB.GetChannel() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "counterparty upgrade sequence < current upgrade sequence (1 < 99): invalid upgrade sequence", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeTryResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -1022,9 +1168,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { tc.malleate() - res, err := suite.chainB.GetSimApp().GetIBCKeeper().ChannelUpgradeTry(suite.chainB.GetContext(), msg) + ctx := suite.chainB.GetContext() + res, err := suite.chainB.GetSimApp().GetIBCKeeper().ChannelUpgradeTry(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } @@ -1038,12 +1186,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) }{ { "success, no pending in-flight packets", func() {}, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -1051,6 +1199,25 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { channel := path.EndpointA.GetChannel() suite.Require().Equal(channeltypes.FLUSHCOMPLETE, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + upgrade := path.EndpointA.GetChannelUpgrade() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeAck: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: upgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1061,7 +1228,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { // Set a dummy packet commitment to simulate in-flight packets suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), portID, channelID, 1, []byte("hash")) }, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -1069,6 +1236,25 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { channel := path.EndpointA.GetChannel() suite.Require().Equal(channeltypes.FLUSHING, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + upgrade := path.EndpointA.GetChannelUpgrade() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeAck: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: upgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: upgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: upgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1077,11 +1263,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { msg.PortId = ibctesting.InvalidID msg.ChannelId = ibctesting.InvalidID }, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) + suite.Require().Empty(events) }, }, { @@ -1093,7 +1280,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, channeltypes.ErrInvalidChannelState) @@ -1101,6 +1288,8 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { errorReceipt, found := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().Empty(errorReceipt) suite.Require().False(found) + + suite.Require().Empty(events) }, }, { @@ -1112,7 +1301,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { path.EndpointA.SetChannelUpgrade(upgrade) }, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1121,6 +1310,24 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { errorReceipt, found := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found) suite.Require().Equal(uint64(1), errorReceipt.Sequence) + + channel := path.EndpointB.GetChannel() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "expected upgrade ordering (ORDER_NONE_UNSPECIFIED) to match counterparty upgrade ordering (ORDER_UNORDERED): incompatible counterparty upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1135,7 +1342,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { return fmt.Errorf("mock app callback failed") } }, - func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1148,6 +1355,42 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { // assert application state changes are not committed store := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetKey(exported.ModuleName)) suite.Require().False(store.Has([]byte("foo"))) + + channel := path.EndpointB.GetChannel() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "mock app callback failed", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeAckResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + suite.Require().Empty(events) }, }, } @@ -1189,9 +1432,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeAck() { tc.malleate() - res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeAck(suite.chainA.GetContext(), msg) + ctx := suite.chainA.GetContext() + res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeAck(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } @@ -1205,12 +1450,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) }{ { "success, no pending in-flight packets", func() {}, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -1218,6 +1463,33 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { channel := path.EndpointB.GetChannel() suite.Require().Equal(channeltypes.OPEN, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeConfirm: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyChannelState: channeltypes.FLUSHCOMPLETE.String(), + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeOpen: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyChannelState: channeltypes.OPEN.String(), + channeltypes.AttributeKeyUpgradeConnectionHops: channel.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: channel.Version, + channeltypes.AttributeKeyUpgradeOrdering: channel.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1261,7 +1533,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { Signer: suite.chainA.SenderAccount.GetAddress().String(), } }, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -1273,6 +1545,19 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { channel = path.EndpointB.GetChannel() suite.Require().Equal(channeltypes.FLUSHCOMPLETE, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeConfirm: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyChannelState: channeltypes.FLUSHCOMPLETE.String(), + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1281,7 +1566,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { portID, channelID := path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID suite.chainB.GetSimApp().IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), portID, channelID, 1, []byte("hash")) }, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(channeltypes.SUCCESS, res.Result) @@ -1289,6 +1574,19 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { channel := path.EndpointB.GetChannel() suite.Require().Equal(channeltypes.FLUSHING, channel.State) suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeConfirm: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyChannelState: channeltypes.FLUSHING.String(), + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1297,11 +1595,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { msg.PortId = ibctesting.InvalidID msg.ChannelId = ibctesting.InvalidID }, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) + suite.Require().Empty(events) }, }, { @@ -1313,7 +1612,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { path.EndpointB.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, channeltypes.ErrInvalidChannelState) @@ -1321,6 +1620,8 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { errorReceipt, found := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) suite.Require().Empty(errorReceipt) suite.Require().False(found) + + suite.Require().Empty(events) }, }, { @@ -1344,7 +1645,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { msg.ProofUpgrade = proofUpgrade msg.ProofHeight = proofHeight }, - func(res *channeltypes.MsgChannelUpgradeConfirmResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1353,6 +1654,44 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { errorReceipt, found := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) suite.Require().True(found) suite.Require().Equal(uint64(1), errorReceipt.Sequence) + + channel := path.EndpointB.GetChannel() + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "counterparty upgrade timeout elapsed: current timestamp: 1578269010000000000, timeout timestamp 1578268995000000000: timeout elapsed", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeConfirmResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -1399,9 +1738,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeConfirm() { tc.malleate() - res, err := suite.chainB.GetSimApp().GetIBCKeeper().ChannelUpgradeConfirm(suite.chainB.GetContext(), msg) + ctx := suite.chainB.GetContext() + res, err := suite.chainB.GetSimApp().GetIBCKeeper().ChannelUpgradeConfirm(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } @@ -1415,17 +1756,35 @@ func (suite *KeeperTestSuite) TestChannelUpgradeOpen() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeOpenResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeOpenResponse, events []abci.Event, err error) }{ { "success", func() {}, - func(res *channeltypes.MsgChannelUpgradeOpenResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeOpenResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) channel := path.EndpointA.GetChannel() suite.Require().Equal(channeltypes.OPEN, channel.State) + + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeOpen: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyChannelState: channeltypes.OPEN.String(), + channeltypes.AttributeKeyUpgradeConnectionHops: channel.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: channel.Version, + channeltypes.AttributeKeyUpgradeOrdering: channel.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1434,11 +1793,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeOpen() { msg.PortId = ibctesting.InvalidID msg.ChannelId = ibctesting.InvalidID }, - func(res *channeltypes.MsgChannelUpgradeOpenResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeOpenResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) + suite.Require().Empty(events) }, }, { @@ -1448,11 +1808,31 @@ func (suite *KeeperTestSuite) TestChannelUpgradeOpen() { channel.State = channeltypes.FLUSHING path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeOpenResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeOpenResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) suite.Require().ErrorIs(err, channeltypes.ErrInvalidChannelState) + suite.Require().Empty(events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeOpenResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -1499,9 +1879,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeOpen() { tc.malleate() - res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeOpen(suite.chainA.GetContext(), msg) + ctx := suite.chainA.GetContext() + res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeOpen(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } @@ -1515,12 +1897,12 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) }{ { "success: keeper is not authority, valid error receipt so channnel changed to match error receipt seq", func() {}, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1529,6 +1911,34 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should be changed to match sequence on error receipt. suite.Require().Equal(uint64(2), channel.UpgradeSequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeCancel: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "invalid upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1541,7 +1951,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { channel.UpgradeSequence = uint64(3) path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1550,6 +1960,34 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should be changed to match initial upgrade sequence. suite.Require().Equal(uint64(3), channel.UpgradeSequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeCancel: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "invalid upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1563,7 +2001,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { channel.UpgradeSequence = uint64(1) path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1572,6 +2010,34 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should be changed to match initial upgrade sequence. suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeCancel: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "invalid upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1585,7 +2051,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { channel.UpgradeSequence = uint64(1) path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1594,6 +2060,34 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should be changed to match initial upgrade sequence. suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeCancel: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "invalid upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1606,7 +2100,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { channel.UpgradeSequence = uint64(1) path.EndpointA.SetChannel(channel) }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().NoError(err) suite.Require().NotNil(res) @@ -1615,6 +2109,34 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should be changed to match error receipt sequence. suite.Require().Equal(uint64(2), channel.UpgradeSequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeCancel: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "invalid upgrade", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1622,7 +2144,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { func() { msg.ChannelId = ibctesting.InvalidID }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) @@ -1631,6 +2153,8 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.OPEN, channel.State) // Upgrade sequence should not be changed. suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + suite.Require().Empty(events) }, }, { @@ -1640,7 +2164,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { // Force set to STATE_FLUSHCOMPLETE to check that state is not changed. suite.Require().NoError(path.EndpointA.SetChannelState(channeltypes.FLUSHCOMPLETE)) }, - func(res *channeltypes.MsgChannelUpgradeCancelResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().Nil(res) @@ -1650,6 +2174,27 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { suite.Require().Equal(channeltypes.FLUSHCOMPLETE, channel.State) // Upgrade sequence should not be changed. suite.Require().Equal(uint64(1), channel.UpgradeSequence) + + suite.Require().Empty(events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeCancelResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -1703,8 +2248,11 @@ func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { tc.malleate() - res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeCancel(suite.chainA.GetContext(), msg) - tc.expResult(res, err) + ctx := suite.chainA.GetContext() + res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeCancel(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() + + tc.expResult(res, events, err) }) } } @@ -1725,7 +2273,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { cases := []struct { name string malleate func() - expResult func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, err error) + expResult func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, events []abci.Event, err error) }{ { "success", @@ -1740,8 +2288,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { msg.ProofChannel = channelProof msg.ProofHeight = proofHeight }, - func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, err error) { - suite.Require().NoError(err) + func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, events []abci.Event, err error) { channel := path.EndpointA.GetChannel() suite.Require().Equalf(channeltypes.OPEN, channel.State, "channel state should be %s", channeltypes.OPEN.String()) @@ -1750,6 +2297,42 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { suite.Require().False(found, "channel upgrade should be nil") suite.Require().NotNil(res) + suite.Require().NoError(err) + + errorReceipt, found := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + suite.Require().True(found) + suite.Require().Equal(uint64(1), errorReceipt.Sequence) + + // we need to find the event values from the proposed upgrade as the actual upgrade has been deleted. + proposedUpgrade := path.EndpointA.GetProposedUpgrade() + // use the timeout we set in the malleate function + timeout := channeltypes.NewTimeout(clienttypes.ZeroHeight(), 1) + expEvents := ibctesting.EventsMap{ + channeltypes.EventTypeChannelUpgradeTimeout: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeConnectionHops: proposedUpgrade.Fields.ConnectionHops[0], + channeltypes.AttributeKeyUpgradeVersion: proposedUpgrade.Fields.Version, + channeltypes.AttributeKeyUpgradeOrdering: proposedUpgrade.Fields.Ordering.String(), + channeltypes.AttributeKeyUpgradeTimeout: timeout.String(), + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + }, + channeltypes.EventTypeChannelUpgradeError: { + channeltypes.AttributeKeyPortID: path.EndpointA.ChannelConfig.PortID, + channeltypes.AttributeKeyChannelID: path.EndpointA.ChannelID, + channeltypes.AttributeCounterpartyPortID: path.EndpointB.ChannelConfig.PortID, + channeltypes.AttributeCounterpartyChannelID: path.EndpointB.ChannelID, + channeltypes.AttributeKeyUpgradeSequence: fmt.Sprintf("%d", channel.UpgradeSequence), + // need to manually insert this because the errorReceipt is a string constant as it is written into state + channeltypes.AttributeKeyUpgradeErrorReceipt: "upgrade timed-out", + }, + sdk.EventTypeMessage: { + sdk.AttributeKeyModule: channeltypes.AttributeValueCategory, + }, + } + ibctesting.AssertEventsLegacy(&suite.Suite, expEvents, events) }, }, { @@ -1757,7 +2340,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { func() { msg.ChannelId = ibctesting.InvalidID }, - func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) @@ -1767,6 +2350,8 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { _, found := path.EndpointA.Chain.GetSimApp().IBCKeeper.ChannelKeeper.GetUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found, "channel upgrade should not be nil") + + suite.Require().Empty(events) }, }, { @@ -1781,7 +2366,7 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { msg.ProofHeight = proofHeight msg.ProofChannel = []byte("invalid proof") }, - func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, err error) { + func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, events []abci.Event, err error) { suite.Require().Error(err) suite.Require().ErrorIs(err, commitmenttypes.ErrInvalidProof) @@ -1791,6 +2376,27 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { _, found := path.EndpointA.Chain.GetSimApp().IBCKeeper.ChannelKeeper.GetUpgrade(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.Require().True(found, "channel upgrade should not be nil") + + suite.Require().Empty(events) + }, + }, + { + "ibc application does not implement the UpgradeableModule interface", + func() { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + path.EndpointB.ChannelConfig.PortID = ibcmock.MockBlockUpgrade + + suite.coordinator.Setup(path) + + msg.PortId = path.EndpointB.ChannelConfig.PortID + msg.ChannelId = path.EndpointB.ChannelID + }, + func(res *channeltypes.MsgChannelUpgradeTimeoutResponse, events []abci.Event, err error) { + suite.Require().ErrorIs(err, porttypes.ErrInvalidRoute) + suite.Require().Nil(res) + + suite.Require().Empty(events) }, }, } @@ -1826,8 +2432,9 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTimeout() { ctx := suite.chainA.GetContext() res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeTimeout(ctx, msg) + events := ctx.EventManager().Events().ToABCIEvents() - tc.expResult(res, err) + tc.expResult(res, events, err) }) } } diff --git a/modules/core/types/events.go b/modules/core/types/events.go new file mode 100644 index 00000000000..be5c20efe12 --- /dev/null +++ b/modules/core/types/events.go @@ -0,0 +1,3 @@ +package types + +const ErrorAttributeKeyPrefix = "ibccallbackerror-" diff --git a/modules/light-clients/07-tendermint/tendermint.pb.go b/modules/light-clients/07-tendermint/tendermint.pb.go index 16afd1764e0..fd984673584 100644 --- a/modules/light-clients/07-tendermint/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/tendermint.pb.go @@ -38,7 +38,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ClientState struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` TrustLevel Fraction `protobuf:"bytes,2,opt,name=trust_level,json=trustLevel,proto3" json:"trust_level"` - // duration of the period since the LastestTimestamp during which the + // duration of the period since the LatestTimestamp during which the // submitted headers are valid for upgrade TrustingPeriod time.Duration `protobuf:"bytes,3,opt,name=trusting_period,json=trustingPeriod,proto3,stdduration" json:"trusting_period"` // duration of the staking unbonding period diff --git a/modules/light-clients/08-wasm/go.mod b/modules/light-clients/08-wasm/go.mod index 0ad11bdbcb4..7fad581917a 100644 --- a/modules/light-clients/08-wasm/go.mod +++ b/modules/light-clients/08-wasm/go.mod @@ -11,17 +11,17 @@ require ( cosmossdk.io/client/v2 v2.0.0-beta.1 cosmossdk.io/collections v0.4.0 cosmossdk.io/core v0.11.0 - cosmossdk.io/errors v1.0.0 - cosmossdk.io/log v1.2.1 + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.0 cosmossdk.io/math v1.2.0 - cosmossdk.io/store v1.0.1 + cosmossdk.io/store v1.0.2 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/circuit v0.1.0 cosmossdk.io/x/evidence v0.1.0 cosmossdk.io/x/feegrant v0.1.0 cosmossdk.io/x/tx v0.13.0 cosmossdk.io/x/upgrade v0.1.1 - github.com/CosmWasm/wasmvm v1.5.0 + github.com/CosmWasm/wasmvm v1.5.1 github.com/cometbft/cometbft v0.38.2 github.com/cosmos/cosmos-db v1.0.0 github.com/cosmos/cosmos-sdk v0.50.2 @@ -34,7 +34,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f google.golang.org/grpc v1.60.1 ) @@ -188,8 +188,8 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.153.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/modules/light-clients/08-wasm/go.sum b/modules/light-clients/08-wasm/go.sum index 39d7556cd8d..75ca0de1748 100644 --- a/modules/light-clients/08-wasm/go.sum +++ b/modules/light-clients/08-wasm/go.sum @@ -194,14 +194,14 @@ cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= -cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= -cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= -cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= -cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.0 h1:L0Z0XstClo2kOU4h3V1iDoE5Ji64sg5HLOogzGg67Oo= +cosmossdk.io/log v1.3.0/go.mod h1:HIDyvWLqZe2ovlWabsDN4aPMpY/nUEquAhgfTf2ZzB8= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= -cosmossdk.io/store v1.0.1 h1:XBDhCqlL+2MUgE8CHWwndKVJ4beX+TyaPIjB5SV62dM= -cosmossdk.io/store v1.0.1/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/tools/confix v0.1.1 h1:aexyRv9+y15veH3Qw16lxQwo+ki7r2I+g0yNTEFEQM8= cosmossdk.io/tools/confix v0.1.1/go.mod h1:nQVvP1tHsGXS83PonPVWJtSbddIqyjEw99L4M3rPJyQ= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= @@ -225,8 +225,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= -github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= +github.com/CosmWasm/wasmvm v1.5.1 h1:2MHN9uFyHP6pxfvpBJ0JW6ujvAIBk9kQk283zyri0Ro= +github.com/CosmWasm/wasmvm v1.5.1/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= @@ -1580,12 +1580,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 h1:kzJAXnzZoFbe5bhZd4zjUuHos/I31yH4thfMb/13oVY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/modules/light-clients/08-wasm/keeper/keeper_test.go b/modules/light-clients/08-wasm/keeper/keeper_test.go index d6d26173419..73810c74b42 100644 --- a/modules/light-clients/08-wasm/keeper/keeper_test.go +++ b/modules/light-clients/08-wasm/keeper/keeper_test.go @@ -79,8 +79,6 @@ func (suite *KeeperTestSuite) SetupWasmWithMockVM() { suite.coordinator = ibctesting.NewCoordinator(suite.T(), 1) suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - - wasmtesting.AllowWasmClients(suite.chainA) } func (suite *KeeperTestSuite) setupWasmWithMockVM() (ibctesting.TestingApp, map[string]json.RawMessage) { diff --git a/modules/light-clients/08-wasm/testing/wasm_endpoint.go b/modules/light-clients/08-wasm/testing/wasm_endpoint.go index 5d391e547a8..6d126ea35c9 100644 --- a/modules/light-clients/08-wasm/testing/wasm_endpoint.go +++ b/modules/light-clients/08-wasm/testing/wasm_endpoint.go @@ -50,12 +50,3 @@ func (endpoint *WasmEndpoint) CreateClient() error { return nil } - -// AllowWasmClients adds 08-wasm to the list of allowed clients -func AllowWasmClients(chain *ibctesting.TestChain) { - ctx := chain.GetContext() - clientKeeper := chain.App.GetIBCKeeper().ClientKeeper - params := clientKeeper.GetParams(ctx) - params.AllowedClients = append(params.AllowedClients, types.Wasm) - clientKeeper.SetParams(ctx, params) -} diff --git a/modules/light-clients/08-wasm/types/types_test.go b/modules/light-clients/08-wasm/types/types_test.go index 866f0535a6f..e1729f66997 100644 --- a/modules/light-clients/08-wasm/types/types_test.go +++ b/modules/light-clients/08-wasm/types/types_test.go @@ -79,8 +79,6 @@ func (suite *TypesTestSuite) SetupWasmWithMockVM() { suite.coordinator = ibctesting.NewCoordinator(suite.T(), 1) suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.checksum = storeWasmCode(suite, wasmtesting.Code) - - wasmtesting.AllowWasmClients(suite.chainA) } func (suite *TypesTestSuite) setupWasmWithMockVM() (ibctesting.TestingApp, map[string]json.RawMessage) { diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 5053612228c..bd759f4e18c 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -20,7 +20,7 @@ message ClientState { string chain_id = 1; Fraction trust_level = 2 [(gogoproto.nullable) = false]; - // duration of the period since the LastestTimestamp during which the + // duration of the period since the LatestTimestamp during which the // submitted headers are valid for upgrade google.protobuf.Duration trusting_period = 3 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; // duration of the staking unbonding period diff --git a/testing/endpoint.go b/testing/endpoint.go index fcf5099562f..6d85761712f 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -62,7 +62,7 @@ func NewDefaultEndpoint(chain *TestChain) *Endpoint { } } -// QueryProof queries proof associated with this endpoint using the lastest client state +// QueryProof queries proof associated with this endpoint using the latest client state // height on the counterparty chain. func (endpoint *Endpoint) QueryProof(key []byte) ([]byte, clienttypes.Height) { // obtain the counterparty client representing the chain associated with the endpoint diff --git a/testing/mock/events.go b/testing/mock/events.go new file mode 100644 index 00000000000..e92592c0326 --- /dev/null +++ b/testing/mock/events.go @@ -0,0 +1,39 @@ +package mock + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + MockEventTypeRecvPacket = "mock-recv-packet" + MockEventTypeAckPacket = "mock-ack-packet" + MockEventTypeTimeoutPacket = "mock-timeout" + + MockAttributeKey1 = "mock-attribute-key-1" + MockAttributeKey2 = "mock-attribute-key-2" + + MockAttributeValue1 = "mock-attribute-value-1" + MockAttributeValue2 = "mock-attribute-value-2" +) + +// NewMockRecvPacketEvent returns a mock receive packet event +func NewMockRecvPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeRecvPacket) +} + +// NewMockAckPacketEvent returns a mock acknowledgement packet event +func NewMockAckPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeAckPacket) +} + +// NewMockTimeoutPacketEvent emits a mock timeout packet event +func NewMockTimeoutPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeTimeoutPacket) +} + +// emitMockEvent returns a mock event with the given event type +func newMockEvent(eventType string) sdk.Event { + return sdk.NewEvent( + eventType, + sdk.NewAttribute(MockAttributeKey1, MockAttributeValue1), + sdk.NewAttribute(MockAttributeKey2, MockAttributeValue2), + ) +} diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index c0a153918bf..9ca2b23c6d8 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -136,6 +136,8 @@ func (im IBCModule) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, re panic(err) } + ctx.EventManager().EmitEvent(NewMockRecvPacketEvent()) + if bytes.Equal(MockPacketData, packet.GetData()) { return MockAcknowledgement } else if bytes.Equal(MockAsyncPacketData, packet.GetData()) { @@ -158,6 +160,8 @@ func (im IBCModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes panic(err) } + ctx.EventManager().EmitEvent(NewMockAckPacketEvent()) + return nil } @@ -174,22 +178,24 @@ func (im IBCModule) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, panic(err) } + ctx.EventManager().EmitEvent(NewMockTimeoutPacketEvent()) + return nil } // OnChanUpgradeInit implements the IBCModule interface -func (im IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) (string, error) { +func (im IBCModule) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) (string, error) { if im.IBCApp.OnChanUpgradeInit != nil { - return im.IBCApp.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, version) + return im.IBCApp.OnChanUpgradeInit(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } - return version, nil + return proposedVersion, nil } // OnChanUpgradeTry implements the IBCModule interface -func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) { +func (im IBCModule) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, counterpartyVersion string) (string, error) { if im.IBCApp.OnChanUpgradeTry != nil { - return im.IBCApp.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion) + return im.IBCApp.OnChanUpgradeTry(ctx, portID, channelID, proposedOrder, proposedConnectionHops, counterpartyVersion) } return counterpartyVersion, nil @@ -205,9 +211,9 @@ func (im IBCModule) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, counter } // OnChanUpgradeOpen implements the IBCModule interface -func (im IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, version string) { +func (im IBCModule) OnChanUpgradeOpen(ctx sdk.Context, portID, channelID string, proposedOrder channeltypes.Order, proposedConnectionHops []string, proposedVersion string) { if im.IBCApp.OnChanUpgradeOpen != nil { - im.IBCApp.OnChanUpgradeOpen(ctx, portID, channelID, order, connectionHops, version) + im.IBCApp.OnChanUpgradeOpen(ctx, portID, channelID, proposedOrder, proposedConnectionHops, proposedVersion) } } diff --git a/testing/mock/middleware.go b/testing/mock/middleware.go new file mode 100644 index 00000000000..954132d0cff --- /dev/null +++ b/testing/mock/middleware.go @@ -0,0 +1,197 @@ +package mock + +import ( + "bytes" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/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" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + "github.com/cosmos/ibc-go/v8/modules/core/exported" +) + +const ( + MockBlockUpgrade = "mockblockupgrade" +) + +var _ porttypes.Middleware = (*BlockUpgradeMiddleware)(nil) + +// BlockUpgradeMiddleware does not implement the UpgradeableModule interface +type BlockUpgradeMiddleware struct { + appModule *AppModule + IBCApp *IBCApp // base application of an IBC middleware stack +} + +// NewIBCModule creates a new IBCModule given the underlying mock IBC application and scopedKeeper. +func NewBlockUpgradeMiddleware(appModule *AppModule, app *IBCApp) BlockUpgradeMiddleware { + appModule.ibcApps = append(appModule.ibcApps, app) + return BlockUpgradeMiddleware{ + appModule: appModule, + IBCApp: app, + } +} + +// OnChanOpenInit implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanOpenInit( + ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, + channelID string, chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string, +) (string, error) { + if strings.TrimSpace(version) == "" { + version = Version + } + + if im.IBCApp.OnChanOpenInit != nil { + return im.IBCApp.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) + } + + if chanCap != nil { + // Claim channel capability passed back by IBC module + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } + } + + return version, nil +} + +// OnChanOpenTry implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanOpenTry( + ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, + channelID string, chanCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, counterpartyVersion string, +) (version string, err error) { + if im.IBCApp.OnChanOpenTry != nil { + return im.IBCApp.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) + } + + if chanCap != nil { + // Claim channel capability passed back by IBC module + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } + } + + return Version, nil +} + +// OnChanOpenAck implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanOpenAck(ctx sdk.Context, portID string, channelID string, counterpartyChannelID string, counterpartyVersion string) error { + if im.IBCApp.OnChanOpenAck != nil { + return im.IBCApp.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + } + + return nil +} + +// OnChanOpenConfirm implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanOpenConfirm(ctx sdk.Context, portID, channelID string) error { + if im.IBCApp.OnChanOpenConfirm != nil { + return im.IBCApp.OnChanOpenConfirm(ctx, portID, channelID) + } + + return nil +} + +// OnChanCloseInit implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanCloseInit(ctx sdk.Context, portID, channelID string) error { + if im.IBCApp.OnChanCloseInit != nil { + return im.IBCApp.OnChanCloseInit(ctx, portID, channelID) + } + + return nil +} + +// OnChanCloseConfirm implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnChanCloseConfirm(ctx sdk.Context, portID, channelID string) error { + if im.IBCApp.OnChanCloseConfirm != nil { + return im.IBCApp.OnChanCloseConfirm(ctx, portID, channelID) + } + + return nil +} + +// OnRecvPacket implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) exported.Acknowledgement { + if im.IBCApp.OnRecvPacket != nil { + return im.IBCApp.OnRecvPacket(ctx, packet, relayer) + } + + // set state by claiming capability to check if revert happens return + capName := GetMockRecvCanaryCapabilityName(packet) + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } + + if bytes.Equal(MockPacketData, packet.GetData()) { + return MockAcknowledgement + } else if bytes.Equal(MockAsyncPacketData, packet.GetData()) { + return nil + } + + return MockFailAcknowledgement +} + +// OnAcknowledgementPacket implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error { + if im.IBCApp.OnAcknowledgementPacket != nil { + return im.IBCApp.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) + } + + capName := GetMockAckCanaryCapabilityName(packet) + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } + + return nil +} + +// OnTimeoutPacket implements the IBCModule interface. +func (im BlockUpgradeMiddleware) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error { + if im.IBCApp.OnTimeoutPacket != nil { + return im.IBCApp.OnTimeoutPacket(ctx, packet, relayer) + } + + capName := GetMockTimeoutCanaryCapabilityName(packet) + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } + + return nil +} + +// SendPacket implements the ICS4 Wrapper interface +func (BlockUpgradeMiddleware) SendPacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, +) (uint64, error) { + return 0, nil +} + +// WriteAcknowledgement implements the ICS4 Wrapper interface +func (BlockUpgradeMiddleware) WriteAcknowledgement( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, +) error { + return nil +} + +// GetAppVersion returns the application version of the underlying application +func (BlockUpgradeMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + return Version, true +} diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 7c63fc97340..dc44a54bd14 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -341,6 +341,7 @@ func NewSimApp( // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do // not replicate if you do not need to test core IBC or light clients. scopedIBCMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName) + scopedIBCMockBlockUpgradeKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.MockBlockUpgrade) scopedFeeMockKeeper := app.CapabilityKeeper.ScopeToModule(MockFeePort) scopedICAMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName + icacontrollertypes.SubModuleName) @@ -489,6 +490,13 @@ func NewSimApp( app.IBCMockModule = mockIBCModule ibcRouter.AddRoute(ibcmock.ModuleName, mockIBCModule) + // Mock IBC app wrapped with a middleware which does not implement the UpgradeableModule interface. + // NOTE: this is used to test integration with apps which do not yet fulfill the UpgradeableModule interface and error when + // an upgrade is tried on the channel. + mockBlockUpgradeIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp(ibcmock.MockBlockUpgrade, scopedIBCMockBlockUpgradeKeeper)) + mockBlockUpgradeMw := ibcmock.NewBlockUpgradeMiddleware(&mockModule, mockBlockUpgradeIBCModule.IBCApp) + ibcRouter.AddRoute(ibcmock.MockBlockUpgrade, mockBlockUpgradeMw) + // Create Transfer Stack // SendPacket, since it is originating from the application to core IBC: // transferKeeper.SendPacket -> fee.SendPacket -> channel.SendPacket