diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 435e6a89ca8..c1a3ba9d84b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,18 +12,41 @@ are the most critical to review. closes: #XXXX + +### Commit Message / Changelog Entry + +```bash +type: commit message +``` + +see the [guidelines](../CONTRIBUTING.md#commit-messages) for commit messages. (view raw markdown for examples) + + + + --- Before we can merge this PR, please make sure that all the following items have been checked off. If any of the checklist items are not applicable, please leave them but write a little note why. -- [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) +- [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)). - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. -- [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). -- [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) -- [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) +- [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/11-structure.md). +- [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing). +- [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`). - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). -- [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` -- [ ] Re-reviewed `Files changed` in the Github PR explorer -- [ ] Review `Codecov Report` in the comment section below once CI passes +- [ ] Provide a [commit message](../CONTRIBUTING.md#commit-messages) to be used for the changelog entry in the PR description for review. +- [ ] Re-reviewed `Files changed` in the Github PR explorer. +- [ ] Review `Codecov Report` in the comment section below once CI passes. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index aaf7c7b8db3..f9a7c4a9230 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,9 +56,25 @@ jobs: # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main + queries: crypto-com/cosmos-sdk-codeql@main,security-and-quality if: env.GIT_DIFF + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) - - run: make build + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + if: env.GIT_DIFF + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fd1f916d05..dc835effc43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,4 +51,5 @@ jobs: context: . push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + build-args: | + IBC_GO_VERSION=${{ github.ref_name }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e2528c919ec..b738b25b726 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Docker Build - run: docker build . --no-cache + run: docker build . --no-cache --build-arg IBC_GO_VERSION=v4.3.0 split-test-files: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 900ef224f5f..19b9f26ac60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,52 +28,137 @@ Types of changes (Stanzas): "Bug Fixes" for any bug fixes. "Client Breaking" for breaking CLI commands and REST routes used by end-users. "API Breaking" for breaking exported APIs used by developers building on SDK. -"State Machine Breaking" for any changes that result in a different AppState given same genesisState and txList. +"State Machine Breaking" for any changes that result in a different AppState given the same genesisState and txList. Ref: https://keepachangelog.com/en/1.0.0/ --> - # Changelog -## [Unreleased] +All notable changes to this project will be documented in this file. + +## [v4.3.1](https://github.com/cosmos/ibc-go/releases/tag/v4.3.1) - 2023-05-25 + +### Bug Fixes + +* [\#3346](https://github.com/cosmos/ibc-go/pull/3346) Properly handle ordered channels in `UnreceivedPackets` query. + +## [v4.3.0](https://github.com/cosmos/ibc-go/releases/tag/v4.3.0) - 2023-01-24 + +### Dependencies + +* Bump Cosmos SDK to v0.45.12 ([#3049](https://github.com/cosmos/ibc-go/issues/3049)) +* Bump ics23 to v0.9.0 ([#2868](https://github.com/cosmos/ibc-go/issues/2868)) ([#2877](https://github.com/cosmos/ibc-go/issues/2877)) + +### State Machine Breaking + +* Write channel state before invoking app callbacks in ack and confirm channel handshake steps ([#2973](https://github.com/cosmos/ibc-go/issues/2973)) + +### Improvements + +* Save gas on IsFeeEnabled ([#2786](https://github.com/cosmos/ibc-go/issues/2786)) + +### Bug Fixes + +* Check `x/bank` send enabled before escrowing fees ([#2942](https://github.com/cosmos/ibc-go/issues/2942)) ([#2952](https://github.com/cosmos/ibc-go/issues/2952)) + +### Documentation + +* Fix migration/docs for ICA controller middleware ([#2737](https://github.com/cosmos/ibc-go/issues/2737)) ([#2763](https://github.com/cosmos/ibc-go/issues/2763)) + +### Miscellaneous Tasks + +* Integrated git cliff into the code base to automate generation of changelogs ([#2772](https://github.com/cosmos/ibc-go/issues/2772)) +* Updating CHANGELOG and cliff toml + +## [v4.2.0](https://github.com/cosmos/ibc-go/releases/tag/v4.2.0) - 2022-11-07 ### Dependencies -* [\#1525](https://github.com/cosmos/ibc-go/pull/1525) Bump SDK version to v0.45.5 +* [\#2588](https://github.com/cosmos/ibc-go/pull/2588) Bump SDK version to v0.45.10 and Tendermint to v0.34.22. + +### State Machine Breaking + +* (apps/transfer) [\#2651](https://github.com/cosmos/ibc-go/pull/2651) Introduce `mustProtoMarshalJSON` for ics20 packet data marshalling which will skip emission (marshalling) of the memo field if unpopulated (empty). +* (27-interchain-accounts) [\#2580](https://github.com/cosmos/ibc-go/issues/2580) Removing port prefix requirement from the ICA host channel handshake +* (transfer) [\#2377](https://github.com/cosmos/ibc-go/pull/2377) Adding `sequence` to `MsgTransferResponse`. + +### Features + +* (apps/transfer) [\#2595](https://github.com/cosmos/ibc-go/pull/2595) Adding optional memo field to `FungibleTokenPacketData` and `MsgTransfer`. + +### Bug Fixes + +* (apps/transfer) [\#2679](https://github.com/cosmos/ibc-go/pull/2679) Check `x/bank` send enabled. + +## [v4.1.0](https://github.com/cosmos/ibc-go/releases/tag/v4.1.0) - 2022-09-20 + +### Dependencies + +* [\#2288](https://github.com/cosmos/ibc-go/pull/2288) Bump SDK version to v0.45.8 and Tendermint to v0.34.21. + +### Features + +* (apps/27-interchain-accounts) [\#2193](https://github.com/cosmos/ibc-go/pull/2193) Adding `InterchainAccount` gRPC query endpont to ICS27 `controller` submodule to allow users to retrieve registered interchain account addresses. + +### Bug Fixes + +* (27-interchain-accounts) [\#2308](https://github.com/cosmos/ibc-go/pull/2308) Nil checks have been added to ensure services are not registered for nil host or controller keepers. + +## [v4.0.0](https://github.com/cosmos/ibc-go/releases/tag/v4.0.0) - 2022-08-12 + +### Dependencies + +* [\#1627](https://github.com/cosmos/ibc-go/pull/1627) Bump Go version to 1.18 +* [\#1905](https://github.com/cosmos/ibc-go/pull/1905) Bump SDK version to v0.45.7 ### API Breaking +* (core/04-channel) [\#1792](https://github.com/cosmos/ibc-go/pull/1792) Remove `PreviousChannelID` from `NewMsgChannelOpenTry` arguments. `MsgChannelOpenTry.ValidateBasic()` returns error if the deprecated `PreviousChannelID` is not empty. +* (core/03-connection) [\#1797](https://github.com/cosmos/ibc-go/pull/1797) Remove `PreviousConnectionID` from `NewMsgConnectionOpenTry` arguments. `MsgConnectionOpenTry.ValidateBasic()` returns error if the deprecated `PreviousConnectionID` is not empty. +* (modules/core/03-connection) [\#1672](https://github.com/cosmos/ibc-go/pull/1672) Remove crossing hellos from connection handshakes. The `PreviousConnectionId` in `MsgConnectionOpenTry` has been deprecated. +* (modules/core/04-channel) [\#1317](https://github.com/cosmos/ibc-go/pull/1317) Remove crossing hellos from channel handshakes. The `PreviousChannelId` in `MsgChannelOpenTry` has been deprecated. * (transfer) [\#1250](https://github.com/cosmos/ibc-go/pull/1250) Deprecate `GetTransferAccount` since the `transfer` module account is never used. -* (channel) [\#1283](https://github.com/cosmos/ibc-go/pull/1283) The `OnChanOpenInit` application callback now returns a version string in line with the latest [spec changes](https://github.com/cosmos/ibc/pull/629). +* (channel) [\#1283](https://github.com/cosmos/ibc-go/pull/1283) The `OnChanOpenInit` application callback now returns a version string in line with the latest [spec changes](https://github.com/cosmos/ibc/pull/629). * (modules/29-fee)[\#1338](https://github.com/cosmos/ibc-go/pull/1338) Renaming `Result` field in `IncentivizedAcknowledgement` to `AppAcknowledgement`. * (modules/29-fee)[\#1343](https://github.com/cosmos/ibc-go/pull/1343) Renaming `KeyForwardRelayerAddress` to `KeyRelayerAddressForAsyncAck`, and `ParseKeyForwardRelayerAddress` to `ParseKeyRelayerAddressForAsyncAck`. * (apps/27-interchain-accounts)[\#1432](https://github.com/cosmos/ibc-go/pull/1432) Updating `RegisterInterchainAccount` to include an additional `version` argument, supporting ICS29 fee middleware functionality in ICS27 interchain accounts. * (apps/27-interchain-accounts)[\#1565](https://github.com/cosmos/ibc-go/pull/1565) Removing `NewErrorAcknowledgement` in favour of `channeltypes.NewErrorAcknowledgement`. * (transfer)[\#1565](https://github.com/cosmos/ibc-go/pull/1565) Removing `NewErrorAcknowledgement` in favour of `channeltypes.NewErrorAcknowledgement`. * (channel)[\#1565](https://github.com/cosmos/ibc-go/pull/1565) Updating `NewErrorAcknowledgement` to accept an error instead of a string and removing the possibility of non-deterministic writes to application state. +* (core/04-channel)[\#1636](https://github.com/cosmos/ibc-go/pull/1636) Removing `SplitChannelVersion` and `MergeChannelVersions` functions since they are not used. ### State Machine Breaking +* (apps/transfer) [\#1907](https://github.com/cosmos/ibc-go/pull/1907) Blocked module account addresses are no longer allowed to send IBC transfers. +* (apps/27-interchain-accounts) [\#1882](https://github.com/cosmos/ibc-go/pull/1882) Explicitly check length of interchain account packet data in favour of nil check. + ### Improvements +* (app/20-transfer) [\#1680](https://github.com/cosmos/ibc-go/pull/1680) Adds migration to correct any malformed trace path information of tokens with denoms that contains slashes. The transfer module consensus version has been bumped to 2. +* (app/20-transfer) [\#1730](https://github.com/cosmos/ibc-go/pull/1730) parse the ics20 denomination provided via a packet using the channel identifier format specified by ibc-go. * (cleanup) [\#1335](https://github.com/cosmos/ibc-go/pull/1335/) `gofumpt -w -l .` to standardize the code layout more strictly than `go fmt ./...` -* (middleware) [\#1022](https://github.com/cosmos/ibc-go/pull/1022) Add `GetAppVersion` to the ICS4Wrapper interface. This function should be used by IBC applications to obtain their own version since the version set in the channel structure may be wrapped many times by middleware. +* (middleware) [\#1022](https://github.com/cosmos/ibc-go/pull/1022) Add `GetAppVersion` to the ICS4Wrapper interface. This function should be used by IBC applications to obtain their own version since the version set in the channel structure may be wrapped many times by middleware. * (modules/core/04-channel) [\#1232](https://github.com/cosmos/ibc-go/pull/1232) Updating params on `NewPacketId` and moving to bottom of file. * (app/29-fee) [\#1305](https://github.com/cosmos/ibc-go/pull/1305) Change version string for fee module to `ics29-1` * (app/29-fee) [\#1341](https://github.com/cosmos/ibc-go/pull/1341) Check if the fee module is locked and if the fee module is enabled before refunding all fees -* (testing/simapp) [\#1397](https://github.com/cosmos/ibc-go/pull/1397) Adding mock module to maccperms and adding check to ensure mock module is not a blocked account address. -* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (transfer) [\#1414](https://github.com/cosmos/ibc-go/pull/1414) Emitting Sender address from `fungible_token_packet` events in `OnRecvPacket` and `OnAcknowledgementPacket`. +* (testing/simapp) [\#1397](https://github.com/cosmos/ibc-go/pull/1397) Adding mock module to maccperms and adding check to ensure mock module is not a blocked account address. +* (core/02-client) [\#1570](https://github.com/cosmos/ibc-go/pull/1570) Emitting an event when handling an upgrade client proposal. +* (modules/light-clients/07-tendermint) [\#1713](https://github.com/cosmos/ibc-go/pull/1713) Allow client upgrade proposals to update `TrustingPeriod`. See ADR-026 for context. +* (core/client) [\#1740](https://github.com/cosmos/ibc-go/pull/1740) Add `cosmos_proto.implements_interface` to adhere to guidelines in [Cosmos SDK ADR 019](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-019-protobuf-state-encoding.md#safe-usage-of-any) for annotating `google.protobuf.Any` types ### Features * [\#276](https://github.com/cosmos/ibc-go/pull/276) Adding the Fee Middleware module v1 -* (apps/29-fee) [\#1229](https://github.com/cosmos/ibc-go/pull/1229) Adding CLI commands for getting all unrelayed incentivized packets and packet by packet-id. +* (apps/29-fee) [\#1229](https://github.com/cosmos/ibc-go/pull/1229) Adding CLI commands for getting all unrelayed incentivized packets and packet by packet-id. * (apps/29-fee) [\#1224](https://github.com/cosmos/ibc-go/pull/1224) Adding Query/CounterpartyAddress and CLI to ICS29 fee middleware * (apps/29-fee) [\#1225](https://github.com/cosmos/ibc-go/pull/1225) Adding Query/FeeEnabledChannel and Query/FeeEnabledChannels with CLIs to ICS29 fee middleware. -* (modules/apps/29-fee) [\#1230](https://github.com/cosmos/ibc-go/pull/1230) Adding CLI command for getting incentivized packets for a specific channel-id. +* (modules/apps/29-fee) [\#1230](https://github.com/cosmos/ibc-go/pull/1230) Adding CLI command for getting incentivized packets for a specific channel-id. ### Bug Fixes +* (apps/29-fee) [\#1774](https://github.com/cosmos/ibc-go/pull/1774) Change non nil relayer assertion to non empty to avoid import/export issues for genesis upgrades. * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specific channel did not follow the same format as the rest of queries. +* (modules/core/04-channel)[\#1919](https://github.com/cosmos/ibc-go/pull/1919) Fixed formatting of sequence for packet "acknowledgement written" logs. ## [v3.1.0](https://github.com/cosmos/ibc-go/releases/tag/v3.1.0) - 2022-04-16 @@ -81,10 +166,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#1300](https://github.com/cosmos/ibc-go/pull/1300) Bump SDK version to v0.45.4 -### API Breaking - -### State Machine Breaking - ### Improvements * (transfer) [\#1342](https://github.com/cosmos/ibc-go/pull/1342) `DenomTrace` grpc now takes in either an `ibc denom` or a `hash` instead of only accepting a `hash`. @@ -98,7 +179,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features * (modules/core/02-client) [\#1336](https://github.com/cosmos/ibc-go/pull/1336) Adding Query/ConsensusStateHeights gRPC for fetching the height of every consensus state associated with a client. -* (modules/apps/transfer) [\#1416](https://github.com/cosmos/ibc-go/pull/1416) Adding gRPC endpoint for getting an escrow account for a given port-id and channel-id. +* (modules/apps/transfer) [\#1416](https://github.com/cosmos/ibc-go/pull/1416) Adding gRPC endpoint for getting an escrow account for a given port-id and channel-id. * (modules/apps/27-interchain-accounts) [\#1512](https://github.com/cosmos/ibc-go/pull/1512) Allowing ICA modules to handle all message types with "*". ### Bug Fixes @@ -146,7 +227,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer. * (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) `GetSelfConsensusState` return type changed from bool to error. * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. -* (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks +* (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks * (testing) [\#892](https://github.com/cosmos/ibc-go/pull/892) IBC Mock modules store the scoped keeper and portID within the IBCMockApp. They also maintain reference to the AppModule to update the AppModule's list of IBC applications it references. Allows for the mock module to be reused as a base application in middleware stacks. * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array * (modules/core/ante) [\#950](https://github.com/cosmos/ibc-go/pull/950) Replaces the channel keeper with the IBC keeper in the IBC `AnteDecorator` in order to execute the entire message and be able to reject redundant messages that are in the same block as the non-redundant messages. @@ -166,14 +247,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts. * [\#383](https://github.com/cosmos/ibc-go/pull/383) Adds helper functions for merging and splitting middleware versions from the underlying app version. * (modules/core/05-port) [\#288](https://github.com/cosmos/ibc-go/issues/288) Making the 05-port keeper function IsBound public. The IsBound function checks if the provided portID is already binded to a module. -* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel. -* (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks. -* (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. +* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel. +* (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks. +* (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. * (interchain-accounts) [\#1466](https://github.com/cosmos/ibc-go/pull/1466) Emit event when there is an acknowledgement during `OnRecvPacket`. ### Features -* [\#432](https://github.com/cosmos/ibc-go/pull/432) Introduce `MockIBCApp` struct to the mock module. Allows the mock module to be reused to perform custom logic on each IBC App interface function. This might be useful when testing out IBC applications written as middleware. +* [\#432](https://github.com/cosmos/ibc-go/pull/432) Introduce `MockIBCApp` struct to the mock module. Allows the mock module to be reused to perform custom logic on each IBC App interface function. This might be useful when testing out IBC applications written as middleware. * [\#380](https://github.com/cosmos/ibc-go/pull/380) Adding the Interchain Accounts module v1 * [\#679](https://github.com/cosmos/ibc-go/pull/679) New CLI command `query ibc-transfer denom-hash ` to get the denom hash for a denom trace; this might be useful for debug @@ -309,7 +390,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking * (core) [\#227](https://github.com/cosmos/ibc-go/pull/227) Remove sdk.Result from application callbacks -* (transfer) [\#350](https://github.com/cosmos/ibc-go/pull/350) Change FungibleTokenPacketData to use a string for the Amount field. This enables token transfers with amounts previously restricted by uint64. Up to the maximum uint256 value is supported. +* (transfer) [\#350](https://github.com/cosmos/ibc-go/pull/350) Change FungibleTokenPacketData to use a string for the Amount field. This enables token transfers with amounts previously restricted by uint64. Up to the maximum uint256 value is supported. ### Features @@ -334,7 +415,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features * (modules/core/02-client) [\#1336](https://github.com/cosmos/ibc-go/pull/1336) Adding Query/ConsensusStateHeights gRPC for fetching the height of every consensus state associated with a client. -* (modules/apps/transfer) [\#1416](https://github.com/cosmos/ibc-go/pull/1416) Adding gRPC endpoint for getting an escrow account for a given port-id and channel-id. +* (modules/apps/transfer) [\#1416](https://github.com/cosmos/ibc-go/pull/1416) Adding gRPC endpoint for getting an escrow account for a given port-id and channel-id. ### Bug Fixes @@ -499,7 +580,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [v1.1.2](https://github.com/cosmos/ibc-go/releases/tag/v1.1.2) - 2021-10-15 * [\#485](https://github.com/cosmos/ibc-go/pull/485) Bump SDK version to v0.44.2 - + ## [v1.1.1](https://github.com/cosmos/ibc-go/releases/tag/v1.1.1) - 2021-10-04 ### Dependencies @@ -530,8 +611,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (core) [\#200](https://github.com/cosmos/ibc-go/pull/200) Fixes incorrect export of IBC identifier sequences. Previously, the next identifier sequence for clients/connections/channels was not set during genesis export. This resulted in the next identifiers being generated on the new chain to reuse old identifiers (the sequences began again from 0). * (02-client) [\#192](https://github.com/cosmos/ibc-go/pull/192) Fix IBC `query ibc client header` cli command. Support historical queries for query header/node-state commands. * (modules/light-clients/06-solomachine) [\#153](https://github.com/cosmos/ibc-go/pull/153) Fix solo machine proof height sequence mismatch bug. -* (modules/light-clients/06-solomachine) [\#122](https://github.com/cosmos/ibc-go/pull/122) Fix solo machine merkle prefix casting bug. -* (modules/light-clients/06-solomachine) [\#120](https://github.com/cosmos/ibc-go/pull/120) Fix solo machine handshake verification bug. +* (modules/light-clients/06-solomachine) [\#122](https://github.com/cosmos/ibc-go/pull/122) Fix solo machine merkle prefix casting bug. +* (modules/light-clients/06-solomachine) [\#120](https://github.com/cosmos/ibc-go/pull/120) Fix solo machine handshake verification bug. * (modules/light-clients/06-solomachine) [\#153](https://github.com/cosmos/ibc-go/pull/153) fix solo machine connection handshake failure at `ConnectionOpenAck`. ### API Breaking @@ -540,19 +621,19 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules) [\#206](https://github.com/cosmos/ibc-go/pull/206) Expose `relayer sdk.AccAddress` on `OnRecvPacket`, `OnAcknowledgementPacket`, `OnTimeoutPacket` module callbacks to enable incentivization. * (02-client) [\#181](https://github.com/cosmos/ibc-go/pull/181) Remove 'InitialHeight' from UpdateClient Proposal. Only copy over latest consensus state from substitute client. * (06-solomachine) [\#169](https://github.com/cosmos/ibc-go/pull/169) Change FrozenSequence to boolean in solomachine ClientState. The solo machine proto package has been bumped from `v1` to `v2`. -* (module/core/02-client) [\#165](https://github.com/cosmos/ibc-go/pull/165) Remove GetFrozenHeight from the ClientState interface. +* (module/core/02-client) [\#165](https://github.com/cosmos/ibc-go/pull/165) Remove GetFrozenHeight from the ClientState interface. * (modules) [\#166](https://github.com/cosmos/ibc-go/pull/166) Remove GetHeight from the misbehaviour interface. The `consensus_height` attribute has been removed from Misbehaviour events. -* (modules) [\#162](https://github.com/cosmos/ibc-go/pull/162) Remove deprecated Handler types in core IBC and the ICS 20 transfer module. +* (modules) [\#162](https://github.com/cosmos/ibc-go/pull/162) Remove deprecated Handler types in core IBC and the ICS 20 transfer module. * (modules/core) [\#161](https://github.com/cosmos/ibc-go/pull/161) Remove Type(), Route(), GetSignBytes() from 02-client, 03-connection, and 04-channel messages. * (modules) [\#140](https://github.com/cosmos/ibc-go/pull/140) IsFrozen() client state interface changed to Status(). gRPC `ClientStatus` route added. * (modules/core) [\#109](https://github.com/cosmos/ibc-go/pull/109) Remove connection and channel handshake CLI commands. -* (modules) [\#107](https://github.com/cosmos/ibc-go/pull/107) Modify OnRecvPacket callback to return an acknowledgement which indicates if it is successful or not. Callback state changes are discarded for unsuccessful acknowledgements only. +* (modules) [\#107](https://github.com/cosmos/ibc-go/pull/107) Modify OnRecvPacket callback to return an acknowledgement which indicates if it is successful or not. Callback state changes are discarded for unsuccessful acknowledgements only. * (modules) [\#108](https://github.com/cosmos/ibc-go/pull/108) All message constructors take the signer as a string to prevent upstream bugs. The `String()` function for an SDK Acc Address relies on external context. * (transfer) [\#275](https://github.com/cosmos/ibc-go/pull/275) Remove 'ChanCloseInit' function from transfer keeper. ICS20 does not close channels. ### State Machine Breaking -* (modules/light-clients/07-tendermint) [\#99](https://github.com/cosmos/ibc-go/pull/99) Enforce maximum chain-id length for tendermint client. +* (modules/light-clients/07-tendermint) [\#99](https://github.com/cosmos/ibc-go/pull/99) Enforce maximum chain-id length for tendermint client. * (modules/light-clients/07-tendermint) [\#141](https://github.com/cosmos/ibc-go/pull/141) Allow a new form of misbehaviour that proves counterparty chain breaks time monotonicity, automatically enforce monotonicity in UpdateClient and freeze client if monotonicity is broken. * (modules/light-clients/07-tendermint) [\#141](https://github.com/cosmos/ibc-go/pull/141) Freeze the client if there's a conflicting header submitted for an existing consensus state. * (modules/core/02-client) [\#8405](https://github.com/cosmos/cosmos-sdk/pull/8405) Refactor IBC client update governance proposals to use a substitute client to update a frozen or expired client. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1111fc6d10..292a924d06b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,6 +85,29 @@ All PRs require an approval from at least one CODEOWNER before merge. PRs which - If you sat down with the PR submitter and did a pairing review please note that in the `Approval`, or your PR comments. - If you are only making "surface level" reviews, submit any notes as `Comments` without adding a review. +### Commit Messages + +Commit messages should be [conventional](https://www.conventionalcommits.org/en/v1.0.0/). + +If opening a PR, include the proposed commit message in the PR description. + +The commit message type should be one of: + +* `feat` / `feature` for feature work. +* `bug` / `fix` for bug fixes. +* `imp` / `improvements` for improvements. +* `doc` / `docs` / `documentation` for any documentation changes. +* `test` / `e2e` for addition or improvements of unit, integration and e2e tests or their corresponding infrastructure. +* `deprecated` for deprecation changes. +* `deps` / `build` for changes to dependencies. +* `chore` / `misc` / `nit` for any miscellaneous changes that don't fit into another category. + +**Note**: If any change is breaking, the following format must be used: +* `type` + `(api)!` for api breaking changes, e.g. `fix(api)!: api breaking fix` +* `type` + `(statemachine)!` for state machine breaking changes, e.g. `fix(statemachine)!: state machine breaking fix` + +**`api` breaking changes take precedence over `statemachine` breaking changes.** + ### Updating Documentation If you open a PR on ibc-go, it is mandatory to update the relevant documentation in /docs. diff --git a/Dockerfile b/Dockerfile index f129f823057..3ea85251a72 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,21 @@ FROM golang:1.20 as builder +ARG IBC_GO_VERSION + ENV GOPATH="" ENV GOMODULE="on" +# ensure the ibc go version is being specified for this image. +RUN test -n "${IBC_GO_VERSION}" + COPY go.mod . COPY go.sum . RUN go mod download -ADD testing testing -ADD modules modules -ADD LICENSE LICENSE +COPY testing testing +COPY modules modules +COPY LICENSE LICENSE COPY Makefile . @@ -18,6 +23,10 @@ RUN make build FROM ubuntu:20.04 +ARG IBC_GO_VERSION + +LABEL "org.cosmos.ibc-go" "${IBC_GO_VERSION}" + COPY --from=builder /go/build/simd /bin/simd ENTRYPOINT ["simd"] diff --git a/Makefile b/Makefile index 0ea7f0c10c5..4cd5ec0a75e 100644 --- a/Makefile +++ b/Makefile @@ -207,6 +207,10 @@ view-docs: @cd docs && \ npm install && npm run serve + +changelog: + docker run --rm -v "$$(pwd)"/.git:/app/ -v "$$(pwd)/cliff.toml":/app/cliff.toml orhunp/git-cliff:latest --unreleased --tag $(tag) + .PHONY: build-docs ############################################################################### diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 00000000000..d4406114809 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,121 @@ +# configuration file for git-cliff (0.1.0) + +[changelog] +# changelog header +header = """ + + +# Changelog + +All notable changes to this project will be documented in this file. + +""" +# template for the changelog body +# https://tera.netlify.app/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version }}](https://github.com/cosmos/ibc-go/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + * {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# remove the leading and trailing whitespace from the template +trim = true +# changelog footer +footer = """ + +""" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = true +# regex for preprocessing the commit messages +commit_preprocessors = [ + # A reference to an issue is appened to commits that looks like "(#1234)", this will be replaced + # with a link to that issue, e.g. "[#$1234](https://github.com/cosmos/ibc-go/issues/1234)". + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/cosmos/ibc-go/issues/${2}))" }, + # any reference to a pr like "pr-1234" will be replaced with a link to the PR. + { pattern = '\(pr-([0-9]+)\)', replace = "([#${1}](https://github.com/cosmos/ibc-go/pulls/${1}))" }, + + # the following patterns only exist because "split_commits" is set to true, and we are processesing + # each line of the commit as a separate message. + # these exist to filter out common messages that appear in commit messages that are technically + # conventional, but we do not way to include in the changelog. + { pattern = '^Signed-off-by:.*', replace='' }, + { pattern = '^Co-authored-by:.*', replace='' }, + # don't include references to issues as changelog entries. + { pattern = '^ref:.*', replace='' }, + # exclude CVSS format, CVE can still be included in regular conventinal commits. + { pattern = 'CVSS:.*', replace='' }, + # don't include dependabot auto merge entries. + { pattern = '.*dependabot-automerge-.*', replace='' }, + # don't include statements saying which issue is closed. + { pattern = '^closes:.*', replace='' }, + # remove standalone links in the commit messages. + { pattern = '^https://.*', replace='' }, + # remove lines with html. + { pattern = '^<.*', replace='' }, +] + +# regex for parsing and grouping commits +commit_parsers = [ + # specifying the number in a comment is a workaround to enable ordering of groups. + # these comments are stripped out of the markdown with the filter "{{ group | striptags | trim | upper_first }}" + # above in the body template. + { message = "^((?i)deps|(?i)dep|(?i)build)", group = "Dependencies" }, + { message = '^.*\(api\)!', group = "API Breaking" }, + { message = '^.*\(statemachine\)!', group = "State Machine Breaking" }, + { message = "^((?i)improvements|(?i)imp)", group = "Improvements" }, + { message = "^((?i)feature|(?i)feat)", group = "Features" }, + { message = "^((?i)fix|(?i)bug)", group = "Bug Fixes" }, + { message = "^((?i)doc|(?i)docs|(?i)documentation)", group = "Documentation" }, + { message = "^((?i)test|(?i)e2e)", group = "Testing" }, + { message = "^((?i)deprecated)", group = "Deprecated" }, + { message = "^((?i)chore|(?i)misc|(?i)nit)", group = "Miscellaneous Tasks" }, +] +# filter out the commits that are not matched by commit parsers +filter_commits = false +# glob pattern for matching git tags +tag_pattern = "v[0-9]*" +# regex for skipping tags +skip_tags = "" +# regex for ignoring tags +ignore_tags = "" +# sort the tags chronologically +date_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest" diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index a15407fd54a..f459a997da6 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -3,19 +3,48 @@ module.exports = { title: "IBC-Go", locales: { "/": { - lang: "en-US" + lang: "en-US", }, }, base: process.env.VUEPRESS_BASE || "/", head: [ - ['link', { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" }], - ['link', { rel: "icon", type: "image/png", sizes: "32x32", href: "/favicon-32x32.png" }], - ['link', { rel: "icon", type: "image/png", sizes: "16x16", href: "/favicon-16x16.png" }], - ['link', { rel: "manifest", href: "/site.webmanifest" }], - ['meta', { name: "msapplication-TileColor", content: "#2e3148" }], - ['meta', { name: "theme-color", content: "#ffffff" }], - ['link', { rel: "icon", type: "image/svg+xml", href: "/favicon-svg.svg" }], - ['link', { rel: "apple-touch-icon-precomposed", href: "/apple-touch-icon-precomposed.png" }], + [ + "link", + { + rel: "apple-touch-icon", + sizes: "180x180", + href: "/apple-touch-icon.png", + }, + ], + [ + "link", + { + rel: "icon", + type: "image/png", + sizes: "32x32", + href: "/favicon-32x32.png", + }, + ], + [ + "link", + { + rel: "icon", + type: "image/png", + sizes: "16x16", + href: "/favicon-16x16.png", + }, + ], + ["link", { rel: "manifest", href: "/site.webmanifest" }], + ["meta", { name: "msapplication-TileColor", content: "#2e3148" }], + ["meta", { name: "theme-color", content: "#ffffff" }], + ["link", { rel: "icon", type: "image/svg+xml", href: "/favicon-svg.svg" }], + [ + "link", + { + rel: "apple-touch-icon-precomposed", + href: "/apple-touch-icon-precomposed.png", + }, + ], ], themeConfig: { repo: "cosmos/ibc-go", @@ -32,109 +61,109 @@ module.exports = { //}, versions: [ { - "label": "main", - "key": "main" + label: "main", + key: "main", }, { - "label": "v1.1.0", - "key": "v1.1.0" + label: "v1.1.0", + key: "v1.1.0", }, { - "label": "v1.2.0", - "key": "v1.2.0" + label: "v1.2.0", + key: "v1.2.0", }, { - "label": "v1.3.0", - "key": "v1.3.0" + label: "v1.3.0", + key: "v1.3.0", }, { - "label": "v1.5.0", - "key": "v1.5.0" + label: "v1.5.0", + key: "v1.5.0", }, { - "label": "v1.4.0", - "key": "v1.4.0" + label: "v1.4.0", + key: "v1.4.0", }, { - "label": "v2.0.0", - "key": "v2.0.0" - } , + label: "v2.0.0", + key: "v2.0.0", + }, + { + label: "v2.1.0", + key: "v2.1.0", + }, { - "label": "v2.1.0", - "key": "v2.1.0" - }, - { - "label": "v2.2.0", - "key": "v2.2.0" + label: "v2.2.0", + key: "v2.2.0", }, - { - "label": "v2.3.0", - "key": "v2.3.0" + { + label: "v2.3.0", + key: "v2.3.0", }, { - "label": "v3.0.0", - "key": "v3.0.0" + label: "v3.0.0", + key: "v3.0.0", }, { - "label": "v3.1.0", - "key": "v3.1.0" - } + label: "v3.1.0", + key: "v3.1.0", + }, ], topbar: { - banner: true + banner: true, }, - sidebar: { + sidebar: { auto: false, nav: [ - { + { title: "Using IBC-Go", children: [ { title: "Overview", directory: false, - path: "/ibc/overview.html" - }, + path: "/ibc/overview.html", + }, { title: "Integration", directory: false, - path: "/ibc/integration.html" + path: "/ibc/integration.html", }, { title: "Applications", - directory: false, - path: "/ibc/apps.html" + directory: true, + path: "/ibc/apps", }, { title: "Middleware", directory: true, - path: "/ibc/middleware" + path: "/ibc/middleware", }, { title: "Upgrades", directory: true, - path: "/ibc/upgrades" + path: "/ibc/upgrades", }, { title: "Governance Proposals", directory: false, - path: "/ibc/proposals.html" + path: "/ibc/proposals.html", }, { title: "Relayer", directory: false, - path: "/ibc/relayer.html" + path: "/ibc/relayer.html", }, { title: "Protobuf Documentation", directory: false, - path: "/ibc/proto-docs.html" + path: "/ibc/proto-docs.html", }, { title: "Roadmap", directory: false, - path: "/roadmap/roadmap.html" + path: "/roadmap/roadmap.html", }, - ] + ], }, { title: "IBC Application Modules", @@ -147,34 +176,34 @@ module.exports = { { title: "Overview", directory: false, - path: "/apps/interchain-accounts/overview.html" - }, + path: "/apps/interchain-accounts/overview.html", + }, { title: "Authentication Modules", directory: false, - path: "/apps/interchain-accounts/auth-modules.html" + path: "/apps/interchain-accounts/auth-modules.html", }, { title: "Active Channels", directory: false, - path: "/apps/interchain-accounts/active-channels.html" + path: "/apps/interchain-accounts/active-channels.html", }, { title: "Integration", directory: false, - path: "/apps/interchain-accounts/integration.html" + path: "/apps/interchain-accounts/integration.html", }, { title: "Parameters", directory: false, - path: "/apps/interchain-accounts/parameters.html" + path: "/apps/interchain-accounts/parameters.html", }, { title: "Transactions", directory: false, - path: "/apps/interchain-accounts/transactions.html" + path: "/apps/interchain-accounts/transactions.html", }, - ] + ], }, { title: "Transfer", @@ -184,41 +213,41 @@ module.exports = { { title: "Overview", directory: false, - path: "/apps/transfer/overview.html" - }, + path: "/apps/transfer/overview.html", + }, { title: "State", directory: false, - path: "/apps/transfer/state.html" + path: "/apps/transfer/state.html", }, { title: "State Transitions", directory: false, - path: "/apps/transfer/state-transitions.html" + path: "/apps/transfer/state-transitions.html", }, { title: "Messages", directory: false, - path: "/apps/transfer/messages.html" + path: "/apps/transfer/messages.html", }, { title: "Events", directory: false, - path: "/apps/transfer/events.html" + path: "/apps/transfer/events.html", }, { title: "Metrics", directory: false, - path: "/apps/transfer/metrics.html" + path: "/apps/transfer/metrics.html", }, { title: "Params", directory: false, - path: "/apps/transfer/params.html" + path: "/apps/transfer/params.html", }, - ] + ], }, - ] + ], }, { title: "IBC Middleware Modules", @@ -231,77 +260,78 @@ module.exports = { { title: "Overview", directory: false, - path: "/middleware/ics29-fee/overview.html" - }, - { - title: "Integration", - directory: false, - path: "/middleware/ics29-fee/integration.html" + path: "/middleware/ics29-fee/overview.html", }, { - title: "End Users", + title: "Integration", directory: false, - path: "/middleware/ics29-fee/end-users.html" + path: "/middleware/ics29-fee/integration.html", }, { title: "Fee Messages", directory: false, - path: "/middleware/ics29-fee/msgs.html" + path: "/middleware/ics29-fee/msgs.html", }, { title: "Fee Distribution", directory: false, - path: "/middleware/ics29-fee/fee-distribution.html" + path: "/middleware/ics29-fee/fee-distribution.html", }, { title: "Events", directory: false, - path: "/middleware/ics29-fee/events.html" + path: "/middleware/ics29-fee/events.html", }, - ] + { + title: "End Users", + directory: false, + path: "/middleware/ics29-fee/end-users.html", + }, + ], }, - ] + ], }, { title: "Migrations", children: [ { - title: "Support transfer of coins whose base denom contains slashes", + title: + "Support transfer of coins whose base denom contains slashes", directory: false, - path: "/migrations/support-denoms-with-slashes.html" + path: "/migrations/support-denoms-with-slashes.html", }, { title: "SDK v0.43 to IBC-Go v1", directory: false, - path: "/migrations/sdk-to-v1.html" + path: "/migrations/sdk-to-v1.html", }, { title: "IBC-Go v1 to v2", directory: false, - path: "/migrations/v1-to-v2.html" + path: "/migrations/v1-to-v2.html", }, { title: "IBC-Go v2 to v3", directory: false, - path: "/migrations/v2-to-v3.html" + path: "/migrations/v2-to-v3.html", }, { title: "IBC-Go v3 to v4", directory: false, - path: "/migrations/v3-to-v4.html" + path: "/migrations/v3-to-v4.html", }, - ] + ], }, { title: "Resources", children: [ { title: "IBC Specification", - path: "https://github.com/cosmos/ibc" + path: "https://github.com/cosmos/ibc", }, - ] - } - ] + ], + }, + ], }, gutter: { title: "Help & Support", @@ -310,46 +340,46 @@ module.exports = { title: "Discord", text: "Chat with IBC developers on Discord.", url: "https://discordapp.com/channels/669268347736686612", - bg: "linear-gradient(225.11deg, #2E3148 0%, #161931 95.68%)" + bg: "linear-gradient(225.11deg, #2E3148 0%, #161931 95.68%)", }, github: { title: "Found an Issue?", - text: "Help us improve this page by suggesting edits on GitHub." - } + text: "Help us improve this page by suggesting edits on GitHub.", + }, }, footer: { question: { - text: "Chat with IBC developers in Discord." + text: "Chat with IBC developers in Discord.", }, textLink: { text: "ibcprotocol.org", - url: "https://ibcprotocol.org" + url: "https://ibcprotocol.org", }, services: [ { service: "medium", - url: "https://blog.cosmos.network/" + url: "https://blog.cosmos.network/", }, { service: "twitter", - url: "https://twitter.com/cosmos" + url: "https://twitter.com/cosmos", }, { service: "linkedin", - url: "https://www.linkedin.com/company/interchain-gmbh" + url: "https://www.linkedin.com/company/interchain-gmbh", }, { service: "reddit", - url: "https://reddit.com/r/cosmosnetwork" + url: "https://reddit.com/r/cosmosnetwork", }, { service: "telegram", - url: "https://t.me/cosmosproject" + url: "https://t.me/cosmosproject", }, { service: "youtube", - url: "https://www.youtube.com/c/CosmosProject" - } + url: "https://www.youtube.com/c/CosmosProject", + }, ], smallprint: "The development of IBC-Go is led primarily by [Interchain GmbH](https://interchain.berlin/). Funding for this development comes primarily from the Interchain Foundation, a Swiss non-profit.", @@ -359,64 +389,63 @@ module.exports = { children: [ { title: "Cosmos SDK", - url: "https://docs.cosmos.network" + url: "https://docs.cosmos.network", }, { title: "Cosmos Hub", - url: "https://hub.cosmos.network" + url: "https://hub.cosmos.network", }, { title: "Tendermint Core", - url: "https://docs.tendermint.com" - } - ] + url: "https://docs.tendermint.com", + }, + ], }, { title: "Community", children: [ { title: "Cosmos blog", - url: "https://blog.cosmos.network" + url: "https://blog.cosmos.network", }, { title: "Forum", - url: "https://forum.cosmos.network" + url: "https://forum.cosmos.network", }, { title: "Chat", - url: "https://discord.gg/W8trcGV" - } - ] + url: "https://discord.gg/W8trcGV", + }, + ], }, { title: "Contributing", children: [ { title: "Contributing to the docs", - url: - "https://github.com/cosmos/ibc-go/blob/main/docs/DOCS_README.md" + url: "https://github.com/cosmos/ibc-go/blob/main/docs/DOCS_README.md", }, { title: "Source code on GitHub", - url: "https://github.com/cosmos/ibc-go/" - } - ] - } - ] - } + url: "https://github.com/cosmos/ibc-go/", + }, + ], + }, + ], + }, }, plugins: [ [ "@vuepress/google-analytics", { - ga: "UA-51029217-2" - } + ga: "UA-51029217-2", + }, ], [ "sitemap", { - hostname: "https://ibc.cosmos.network" - } - ] - ] + hostname: "https://ibc.cosmos.network", + }, + ], + ], }; diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index fb6e4f2acc3..348a4e3c3f1 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -96,7 +96,7 @@ We are using [Algolia](https://www.algolia.com) to power full-text search. This ## Consistency Because the build processes are identical (as is the information contained herein), this file should be kept in sync as -much as possible with its [counterpart in the Cosmos SDK repo](https://github.com/cosmos/cosmos-sdk/tree/master/docs/DOCS_README.md). +much as possible with its [counterpart in the Cosmos SDK repo](https://github.com/cosmos/cosmos-sdk/blob/main/docs/README.md). ### Update and Build the RPC docs diff --git a/docs/apps/interchain-accounts/integration.md b/docs/apps/interchain-accounts/integration.md index 14757ccca3c..8bbd7d250aa 100644 --- a/docs/apps/interchain-accounts/integration.md +++ b/docs/apps/interchain-accounts/integration.md @@ -76,7 +76,7 @@ app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - app.AccountKeeper, scopedICAControllerKeeper, app.MsgServiceRouter(), + scopedICAControllerKeeper, app.MsgServiceRouter(), ) app.ICAHostKeeper = icahostkeeper.NewKeeper( appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName), @@ -96,14 +96,15 @@ icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) // ICA auth IBC Module icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) -// Create host and controller IBC Modules as desired -icaControllerIBCModule := icacontroller.NewIBCModule(app.ICAControllerKeeper, icaAuthIBCModule) +// Create controller IBC application stack and host IBC module as desired +icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) // Register host and authentication routes -ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerIBCModule). - AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). - AddRoute(icaauthtypes.ModuleName, icaControllerIBCModule) // Note, the authentication module is routed to the top level of the middleware stack +ibcRouter. + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). + AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack ... @@ -116,12 +117,33 @@ app.moduleManager = module.NewManager( ... +// Add fee middleware to begin blocker logic +app.moduleManager.SetOrderBeginBlockers( + ... + icatypes.ModuleName, + ... +) + +// Add fee middleware to end blocker logic +app.moduleManager.SetOrderEndBlockers( + ... + icatypes.ModuleName, + ... +) + // Add Interchain Accounts module InitGenesis logic -app.mm.SetOrderInitGenesis( +app.moduleManager.SetOrderInitGenesis( ... icatypes.ModuleName, ... ) + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { + ... + paramsKeeper.Subspace(icahosttypes.SubModuleName) + paramsKeeper.Subspace(icacontrollertypes.SubModuleName) + ... ``` ### Using submodules exclusively @@ -156,10 +178,11 @@ app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) -// Create controller IBC Module -icaControllerIBCModule := icacontroller.NewIBCModule(app.ICAControllerKeeper, icaAuthIBCModule) +// Create controller IBC application stack +icaControllerStack := icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) // Register controller and authentication routes -ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerIBCModule) -ibcRouter.AddRoute(icaauthtypes.ModuleName, icaControllerIBCModule) // Note, the authentication module is routed to the top level of the middleware stack +ibcRouter. + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icaauthtypes.ModuleName, icaControllerStack) // Note, the authentication module is routed to the top level of the middleware stack ``` diff --git a/docs/apps/interchain-accounts/overview.md b/docs/apps/interchain-accounts/overview.md index dc015aaf1e3..7aa5ebf6225 100644 --- a/docs/apps/interchain-accounts/overview.md +++ b/docs/apps/interchain-accounts/overview.md @@ -35,4 +35,8 @@ SDK modules on a chain are assumed to be trustworthy. For example, there are no The implementation of ICS27 on ibc-go uses this assumption in its security considerations. The implementation assumes the authentication module will not try to open channels on owner addresses it does not control. -The implementation assumes other IBC application modules will not bind to ports within the ICS27 namespace. +The implementation assumes other IBC application modules will not bind to ports within the ICS27 namespace. + +## Known Bugs + +- Fee-enabled Interchain Accounts channels cannot be reopened in case of closure due to packet timeout. Regular channels (non fee-enabled) can be reopened. A fix for this bug has been implemented, but, since it is API breaking, it is only available from v5.x. See [this PR](https://github.com/cosmos/ibc-go/pull/2302) for more details. diff --git a/docs/apps/transfer/events.md b/docs/apps/transfer/events.md index a538d07d068..52605fd7cfb 100644 --- a/docs/apps/transfer/events.md +++ b/docs/apps/transfer/events.md @@ -23,6 +23,7 @@ order: 5 | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | | fungible_token_packet | success | {ackSuccess} | +| fungible_token_packet | memo | {memo} | | denomination_trace | trace_hash | {hex_hash} | ## `OnAcknowledgePacket` callback @@ -34,6 +35,7 @@ order: 5 | fungible_token_packet | receiver | {receiver} | | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | +| fungible_token_packet | memo | {memo} | | fungible_token_packet | acknowledgement | {ack.String()} | | fungible_token_packet | success | error | {ack.Response} | @@ -45,3 +47,4 @@ order: 5 | fungible_token_packet | refund_receiver | {receiver} | | fungible_token_packet | denom | {denom} | | fungible_token_packet | amount | {amount} | +| fungible_token_packet | memo | {memo} | diff --git a/docs/apps/transfer/messages.md b/docs/apps/transfer/messages.md index 7cb61402e04..26a636de8d2 100644 --- a/docs/apps/transfer/messages.md +++ b/docs/apps/transfer/messages.md @@ -17,6 +17,7 @@ type MsgTransfer struct { Receiver string TimeoutHeight ibcexported.Height TimeoutTimestamp uint64 + Memo string } ``` diff --git a/docs/apps/transfer/metrics.md b/docs/apps/transfer/metrics.md index e9e3086ba1d..7d83b08eb13 100644 --- a/docs/apps/transfer/metrics.md +++ b/docs/apps/transfer/metrics.md @@ -4,7 +4,7 @@ order: 6 # Metrics -The IBC transfer application module exposes the following set of [metrics](https://github.com/cosmos/cosmos-sdk/blob/main/docs/core/telemetry.md). +The IBC transfer application module exposes the following set of [metrics](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/core/09-telemetry.md). | Metric | Description | Unit | Type | |:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------| diff --git a/docs/apps/transfer/params.md b/docs/apps/transfer/params.md index e2e74305311..a04f3d42021 100644 --- a/docs/apps/transfer/params.md +++ b/docs/apps/transfer/params.md @@ -15,10 +15,16 @@ The IBC transfer application module contains the following parameters: The transfers enabled parameter controls send cross-chain transfer capabilities for all fungible tokens. -To prevent a single token from being transferred from the chain, set the `SendEnabled` parameter to `true` and then set the bank module's [`SendEnabled` parameter](https://github.com/cosmos/cosmos-sdk/blob/main/x/bank/spec/05_params.md#sendenabled) for the denomination to `false`. +To prevent a single token from being transferred from the chain, set the `SendEnabled` parameter to `true` and then, depending on the Cosmos SDK version, do one of the following: + +- For Cosmos SDK v0.46.x or earlier, set the bank module's [`SendEnabled` parameter](https://github.com/cosmos/cosmos-sdk/blob/release/v0.46.x/x/bank/spec/05_params.md#sendenabled) for the denomination to `false`. +- For Cosmos SDK versions above v0.46.x, set the bank module's `SendEnabled` entry for the denomination to `false` using `MsgSetSendEnabled` as a governance proposal. ## `ReceiveEnabled` The transfers enabled parameter controls receive cross-chain transfer capabilities for all fungible tokens. -To prevent a single token from being transferred to the chain, set the `ReceiveEnabled` parameter to `true` and then set the bank module's [`SendEnabled` parameter](https://github.com/cosmos/cosmos-sdk/blob/main/x/bank/spec/05_params.md#sendenabled) for the denomination to `false`. +To prevent a single token from being transferred to the chain, set the `ReceiveEnabled` parameter to `true` and then, depending on the Cosmos SDK version, do one of the following: + +- For Cosmos SDK v0.46.x or earlier, set the bank module's [`SendEnabled` parameter](https://github.com/cosmos/cosmos-sdk/blob/release/v0.46.x/x/bank/spec/05_params.md#sendenabled) for the denomination to `false`. +- For Cosmos SDK versions above v0.46.x, set the bank module's `SendEnabled` entry for the denomination to `false` using `MsgSetSendEnabled` as a governance proposal. diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md index 516002ed1b9..bec25a3aad9 100644 --- a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -7,6 +7,7 @@ - 2021/01/15: Revision to support substitute clients for unfreezing - 2021/05/20: Revision to simplify consensus state copying, remove initial height - 2022/04/08: Revision to deprecate AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour +- 2022/07/15: Revision to allow updating of TrustingPeriod ## Status @@ -51,6 +52,9 @@ We elect not to deal with chains which have actually halted, which is necessaril Previously, `AllowUpdateAfterExpiry` and `AllowUpdateAfterMisbehaviour` were used to signal the recovery options for an expired or frozen client, and governance proposals were not allowed to overwrite the client if these parameters were set to false. However, this has now been deprecated because a code migration can overwrite the client and consensus states regardless of the value of these parameters. If governance would vote to overwrite a client or consensus state, it is likely that governance would also be willing to perform a code migration to do the same. + In addition, `TrustingPeriod` was initally not allowed to be updated by a client upgrade proposal. However, due to the number of situations experienced in production where the `TrustingPeriod` of a client should be allowed to be updated because of ie: initial misconfiguration for a canonical channel, governance should be allowed to update this client parameter. + + Note that this should NOT be lightly updated, as there may be a gap in time between when misbehaviour has occured and when the evidence of misbehaviour is submitted. For example, if the `UnbondingPeriod` is 2 weeks and the `TrustingPeriod` has also been set to two weeks, a validator could wait until right before `UnbondingPeriod` finishes, submit false information, then unbond and exit without being slashed for misbehaviour. Therefore, we recommend that the trusting period for the 07-tendermint client be set to 2/3 of the `UnbondingPeriod`. Note that clients frozen due to misbehaviour must wait for the evidence to expire to avoid becoming refrozen. diff --git a/docs/assets/fee-mw/feeflow.png b/docs/assets/fee-mw/feeflow.png new file mode 100644 index 00000000000..ba02071f4d8 Binary files /dev/null and b/docs/assets/fee-mw/feeflow.png differ diff --git a/docs/assets/fee-mw/msgpaypacket.png b/docs/assets/fee-mw/msgpaypacket.png new file mode 100644 index 00000000000..1bd5deb01fd Binary files /dev/null and b/docs/assets/fee-mw/msgpaypacket.png differ diff --git a/docs/assets/fee-mw/paypacketfeeasync.png b/docs/assets/fee-mw/paypacketfeeasync.png new file mode 100644 index 00000000000..27c486a6f82 Binary files /dev/null and b/docs/assets/fee-mw/paypacketfeeasync.png differ diff --git a/docs/assets/fee-mw/registerrelayeraddr.png b/docs/assets/fee-mw/registerrelayeraddr.png new file mode 100644 index 00000000000..29b01da9a7c Binary files /dev/null and b/docs/assets/fee-mw/registerrelayeraddr.png differ diff --git a/docs/client/swagger-ui/swagger.yaml b/docs/client/swagger-ui/swagger.yaml index ca13125f6fa..4a99d829f25 100644 --- a/docs/client/swagger-ui/swagger.yaml +++ b/docs/client/swagger-ui/swagger.yaml @@ -353,6 +353,56 @@ paths: format: byte tags: - Query + '/ibc/apps/interchain_accounts/controller/v1/owners/{owner}/connections/{connection_id}': + get: + summary: >- + InterchainAccount returns the interchain account address for a given + owner address on a given connection + operationId: InterchainAccount + responses: + '200': + description: A successful response. + schema: + type: object + properties: + address: + type: string + description: >- + QueryInterchainAccountResponse the response type for the + Query/InterchainAccount RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: owner + in: path + required: true + type: string + - name: connection_id + in: path + required: true + type: string + tags: + - Query /ibc/apps/interchain_accounts/controller/v1/params: get: summary: Params queries all parameters of the ICA controller submodule. @@ -13232,6 +13282,14 @@ definitions: description: |- Params defines the set of on-chain interchain accounts parameters. The following parameters may be used to disable the controller submodule. + ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountResponse: + type: object + properties: + address: + type: string + description: >- + QueryInterchainAccountResponse the response type for the + Query/InterchainAccount RPC method. ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse: type: object properties: diff --git a/docs/ibc/apps.md b/docs/ibc/apps.md index 267a2e5ca55..76faf1db558 100644 --- a/docs/ibc/apps.md +++ b/docs/ibc/apps.md @@ -73,15 +73,9 @@ OnChanOpenTry( counterparty channeltypes.Counterparty, counterpartyVersion string, ) (string, error) { - // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos - // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) - // If the module can already authenticate the capability then the module already owns it so we don't need to claim - // Otherwise, module does not have channel capability and we must claim it from IBC - if !k.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - // Only claim channel capability passed back by IBC module if we do not already own it - if err := k.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return err - } + // OpenTry must claim the channelCapability that IBC passes into the callback + if err := k.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err } // ... do custom initialization logic @@ -474,4 +468,4 @@ callbacks](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/ibc_ ## Next {hide} -Learn about [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/intro.md) {hide} +Learn about [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/01-intro.md) {hide} diff --git a/docs/ibc/apps/apps.md b/docs/ibc/apps/apps.md new file mode 100644 index 00000000000..86a53e6f94b --- /dev/null +++ b/docs/ibc/apps/apps.md @@ -0,0 +1,51 @@ + + +# IBC Applications + +Learn how to build custom IBC application modules that enable packets to be sent to and received from other IBC-enabled chains. {synopsis} + +This document serves as a guide for developers who want to write their own Inter-blockchain Communication Protocol (IBC) applications for custom use cases. + +Due to the modular design of the IBC protocol, IBC application developers do not need to concern themselves with the low-level details of clients, connections, and proof verification. Nevertheless, an overview of these low-level concepts can be found in [the Overview section](../overview.md). +The document goes into detail on the abstraction layer most relevant for application developers (channels and ports), and describes how to define your own custom packets, `IBCModule` callbacks and more to make an application module IBC ready. + +**To have your module interact over IBC you must:** + +- implement the `IBCModule` interface, i.e.: + - channel (opening) handshake callbacks + - channel closing handshake callbacks + - packet callbacks +- bind to a port(s) +- add keeper methods +- define your own packet data and acknowledgement structs as well as how to encode/decode them +- add a route to the IBC router + +The following sections provide a more detailed explanation of how to write an IBC application +module correctly corresponding to the listed steps. + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +## Working example + +For a real working example of an IBC application, you can look through the `ibc-transfer` module +which implements everything discussed in this section. + +Here are the useful parts of the module to look at: + +[Binding to transfer +port](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/genesis.go) + +[Sending transfer +packets](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/relay.go) + +[Implementing IBC +callbacks](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/ibc_module.go) + +## Next {hide} + +Learn about [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/01-intro.md) {hide} diff --git a/docs/ibc/apps/bindports.md b/docs/ibc/apps/bindports.md new file mode 100644 index 00000000000..c0cfa703191 --- /dev/null +++ b/docs/ibc/apps/bindports.md @@ -0,0 +1,114 @@ + + +# Bind ports + +Learn what changes to make to bind modules to their ports on initialization. {synopsis} + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +Currently, ports must be bound on app initialization. In order to bind modules to their respective ports on initialization, the following needs to be implemented: + +> Note that `portID` does not refer to a certain numerical ID, like `localhost:8080` with a `portID` 8080. Rather it refers to the application module the port binds. For IBC Modules built with the Cosmos SDK, it defaults to the module's name and for Cosmwasm contracts it defaults to the contract address. + +1. Add port ID to the `GenesisState` proto definition: + + ```protobuf + message GenesisState { + string port_id = 1; + // other fields + } + ``` + +1. Add port ID as a key to the module store: + + ```go + // x//types/keys.go + const ( + // ModuleName defines the IBC Module name + ModuleName = "moduleName" + + // Version defines the current version the IBC + // module supports + Version = "moduleVersion-1" + + // PortID is the default port id that module binds to + PortID = "portID" + + // ... + ) + ``` + +1. Add port ID to `x//types/genesis.go`: + + ```go + // in x//types/genesis.go + + // DefaultGenesisState returns a GenesisState with "transfer" as the default PortID. + func DefaultGenesisState() *GenesisState { + return &GenesisState{ + PortId: PortID, + // additional k-v fields + } + } + + // Validate performs basic genesis state validation returning an error upon any + // failure. + func (gs GenesisState) Validate() error { + if err := host.PortIdentifierValidator(gs.PortId); err != nil { + return err + } + //addtional validations + + return gs.Params.Validate() + } + ``` + +1. Bind to port(s) in the module keeper's `InitGenesis`: + + ```go + // InitGenesis initializes the ibc-module state and binds to PortID. + func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { + k.SetPort(ctx, state.PortId) + + // ... + + // Only try to bind to port if it is not already bound, since we may already own + // port capability from capability InitGenesis + if !k.IsBound(ctx, state.PortId) { + // transfer module binds to the transfer port on InitChain + // and claims the returned capability + err := k.BindPort(ctx, state.PortId) + if err != nil { + panic(fmt.Sprintf("could not claim port capability: %v", err)) + } + } + + // ... + } + ``` + + With: + + ```go + // IsBound checks if the module is already bound to the desired port + func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok + } + + // BindPort defines a wrapper function for the port Keeper's function in + // order to expose it to module's InitGenesis function + func (k Keeper) BindPort(ctx sdk.Context, portID string) error { + cap := k.portKeeper.BindPort(ctx, portID) + return k.ClaimCapability(ctx, cap, host.PortPath(portID)) + } + ``` + + The module binds to the desired port(s) and returns the capabilities. + + In the above we find reference to keeper methods that wrap other keeper functionality, in the next section the keeper methods that need to be implemented will be defined. diff --git a/docs/ibc/apps/ibcmodule.md b/docs/ibc/apps/ibcmodule.md new file mode 100644 index 00000000000..d5864435700 --- /dev/null +++ b/docs/ibc/apps/ibcmodule.md @@ -0,0 +1,342 @@ + + +# Implement `IBCModule` interface and callbacks + +Learn how to implement the `IBCModule` interface and all of the callbacks it requires. {synopsis} + +The Cosmos SDK expects all IBC modules to implement the [`IBCModule` +interface](https://github.com/cosmos/ibc-go/tree/main/modules/core/05-port/types/module.go). This interface contains all of the callbacks IBC expects modules to implement. They include callbacks related to channel handshake, closing and packet callbacks (`OnRecvPacket`, `OnAcknowledgementPacket` and `OnTimeoutPacket`). + +```go +// IBCModule implements the ICS26 interface for given the keeper. +// The implementation of the IBCModule interface could for example be in a file called ibc_module.go, +// but ultimately file structure is up to the developer +type IBCModule struct { + keeper keeper.Keeper +} +``` + +Additionally, in the `module.go` file, add the following line: + +```go +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + // Add this line + _ porttypes.IBCModule = IBCModule{} +) +``` + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +## Channel handshake callbacks + +This section will describe the callbacks that are called during channel handshake execution. Among other things, it will claim channel capabilities passed on from core IBC. For a refresher on capabilities, check [the Overview section](../overview.md#capabilities). + +Here are the channel handshake callbacks that modules are expected to implement: + +> Note that some of the code below is _pseudo code_, indicating what actions need to happen but leaving it up to the developer to implement a custom implementation. E.g. the `checkArguments` and `negotiateAppVersion` functions. + +```go +// Called by IBC Handler on MsgOpenInit +func (im IBCModule) OnChanOpenInit(ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + // Examples: + // - Abort if order == UNORDERED, + // - Abort if version is unsupported + if err := checkArguments(args); err != nil { + return "", err + } + + // OpenInit must claim the channelCapability that IBC passes into the callback + if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err + } + + return version, nil +} + +// Called by IBC Handler on MsgOpenTry +func (im IBCModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + if err := checkArguments(args); err != nil { + return "", err + } + + // OpenTry must claim the channelCapability that IBC passes into the callback + if err := im.keeper.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + + // Construct application version + // IBC applications must return the appropriate application version + // This can be a simple string or it can be a complex version constructed + // from the counterpartyVersion and other arguments. + // The version returned will be the channel version used for both channel ends. + appVersion := negotiateAppVersion(counterpartyVersion, args) + + return appVersion, nil +} + +// Called by IBC Handler on MsgOpenAck +func (im IBCModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + if counterpartyVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) + } + + // do custom logic + + return nil +} + +// Called by IBC Handler on MsgOpenConfirm +func (im IBCModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // do custom logic + + return nil +} +``` + +The channel closing handshake will also invoke module callbacks that can return errors to abort the closing handshake. Closing a channel is a 2-step handshake, the initiating chain calls `ChanCloseInit` and the finalizing chain calls `ChanCloseConfirm`. + +```go +// Called by IBC Handler on MsgCloseInit +func (im IBCModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // ... do custom finalization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} + +// Called by IBC Handler on MsgCloseConfirm +func (im IBCModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // ... do custom finalization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} +``` + +### Channel handshake version negotiation + +Application modules are expected to verify versioning used during the channel handshake procedure. + +- `OnChanOpenInit` will verify that the relayer-chosen parameters + are valid and perform any custom `INIT` logic. + It may return an error if the chosen parameters are invalid + in which case the handshake is aborted. + If the provided version string is non-empty, `OnChanOpenInit` should return + the version string if valid or an error if the provided version is invalid. + **If the version string is empty, `OnChanOpenInit` is expected to + return a default version string representing the version(s) + it supports.** + If there is no default version string for the application, + it should return an error if the provided version is an empty string. +- `OnChanOpenTry` will verify the relayer-chosen parameters along with the + counterparty-chosen version string and perform custom `TRY` logic. + If the relayer-chosen parameters + are invalid, the callback must return an error to abort the handshake. + If the counterparty-chosen version is not compatible with this module's + supported versions, the callback must return an error to abort the handshake. + If the versions are compatible, the try callback must select the final version + string and return it to core IBC. + `OnChanOpenTry` may also perform custom initialization logic. +- `OnChanOpenAck` will error if the counterparty selected version string + is invalid and abort the handshake. It may also perform custom ACK logic. + +Versions must be strings but can implement any versioning structure. If your application plans to +have linear releases then semantic versioning is recommended. If your application plans to release +various features in between major releases then it is advised to use the same versioning scheme +as IBC. This versioning scheme specifies a version identifier and compatible feature set with +that identifier. Valid version selection includes selecting a compatible version identifier with +a subset of features supported by your application for that version. The struct used for this +scheme can be found in [03-connection/types](https://github.com/cosmos/ibc-go/blob/main/modules/core/03-connection/types/version.go#L16). + +Since the version type is a string, applications have the ability to do simple version verification +via string matching or they can use the already impelemented versioning system and pass the proto +encoded version into each handhshake call as necessary. + +ICS20 currently implements basic string matching with a single supported version. + +## Packet callbacks + +Just as IBC expects modules to implement callbacks for channel handshakes, it also expects modules to implement callbacks for handling the packet flow through a channel, as defined in the `IBCModule` interface. + +Once a module A and module B are connected to each other, relayers can start relaying packets and acknowledgements back and forth on the channel. + +![IBC packet flow diagram](https://ibcprotocol.org/_nuxt/img/packet_flow.1d89ee0.png) + +Briefly, a successful packet flow works as follows: + +1. module A sends a packet through the IBC module +2. the packet is received by module B +3. if module B writes an acknowledgement of the packet then module A will process the + acknowledgement +4. if the packet is not successfully received before the timeout, then module A processes the + packet's timeout. + +### Sending packets + +Modules **do not send packets through callbacks**, since the modules initiate the action of sending packets to the IBC module, as opposed to other parts of the packet flow where messages sent to the IBC +module must trigger execution on the port-bound module through the use of callbacks. Thus, to send a packet a module simply needs to call `SendPacket` on the `IBCChannelKeeper`. + +> Note that some of the code below is _pseudo code_, indicating what actions need to happen but leaving it up to the developer to implement a custom implementation. E.g. the `EncodePacketData(customPacketData)` function. + +```go +// retrieve the dynamic capability for this channel +channelCap := scopedKeeper.GetCapability(ctx, channelCapName) +// Sending custom application packet data +data := EncodePacketData(customPacketData) +packet.Data = data +// Send packet to IBC, authenticating with channelCap +IBCChannelKeeper.SendPacket(ctx, channelCap, packet) +``` + +::: warning +In order to prevent modules from sending packets on channels they do not own, IBC expects +modules to pass in the correct channel capability for the packet's source channel. +::: + +### Receiving packets + +To handle receiving packets, the module must implement the `OnRecvPacket` callback. This gets +invoked by the IBC module after the packet has been proved valid and correctly processed by the IBC +keepers. Thus, the `OnRecvPacket` callback only needs to worry about making the appropriate state +changes given the packet data without worrying about whether the packet is valid or not. + +Modules may return to the IBC handler an acknowledgement which implements the `Acknowledgement` interface. +The IBC handler will then commit this acknowledgement of the packet so that a relayer may relay the +acknowledgement back to the sender module. + +The state changes that occurred during this callback will only be written if: + +- the acknowledgement was successful as indicated by the `Success()` function of the acknowledgement +- if the acknowledgement returned is nil indicating that an asynchronous process is occurring + +NOTE: Applications which process asynchronous acknowledgements must handle reverting state changes +when appropriate. Any state changes that occurred during the `OnRecvPacket` callback will be written +for asynchronous acknowledgements. + +> Note that some of the code below is _pseudo code_, indicating what actions need to happen but leaving it up to the developer to implement a custom implementation. E.g. the `DecodePacketData(packet.Data)` function. + +```go +func (im IBCModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) ibcexported.Acknowledgement { + // Decode the packet data + packetData := DecodePacketData(packet.Data) + + // do application state changes based on packet data and return the acknowledgement + // NOTE: The acknowledgement will indicate to the IBC handler if the application + // state changes should be written via the `Success()` function. Application state + // changes are only written if the acknowledgement is successful or the acknowledgement + // returned is nil indicating that an asynchronous acknowledgement will occur. + ack := processPacket(ctx, packet, packetData) + + return ack +} +``` + +Reminder, the `Acknowledgement` interface: + +```go +// Acknowledgement defines the interface used to return +// acknowledgements in the OnRecvPacket callback. +type Acknowledgement interface { + Success() bool + Acknowledgement() []byte +} +``` + +### Acknowledging packets + +After a module writes an acknowledgement, a relayer can relay back the acknowledgement to the sender module. The sender module can +then process the acknowledgement using the `OnAcknowledgementPacket` callback. The contents of the +acknowledgement is entirely up to the modules on the channel (just like the packet data); however, it +may often contain information on whether the packet was successfully processed along +with some additional data that could be useful for remediation if the packet processing failed. + +Since the modules are responsible for agreeing on an encoding/decoding standard for packet data and +acknowledgements, IBC will pass in the acknowledgements as `[]byte` to this callback. The callback +is responsible for decoding the acknowledgement and processing it. + +> Note that some of the code below is _pseudo code_, indicating what actions need to happen but leaving it up to the developer to implement a custom implementation. E.g. the `DecodeAcknowledgement(acknowledgments)` and `processAck(ack)` functions. + +```go +func (im IBCModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, +) (*sdk.Result, error) { + // Decode acknowledgement + ack := DecodeAcknowledgement(acknowledgement) + + // process ack + res, err := processAck(ack) + return res, err +} +``` + +### Timeout packets + +If the timeout for a packet is reached before the packet is successfully received or the +counterparty channel end is closed before the packet is successfully received, then the receiving +chain can no longer process it. Thus, the sending chain must process the timeout using +`OnTimeoutPacket` to handle this situation. Again the IBC module will verify that the timeout is +indeed valid, so our module only needs to implement the state machine logic for what to do once a +timeout is reached and the packet can no longer be received. + +```go +func (im IBCModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) (*sdk.Result, error) { + // do custom timeout logic +} +``` diff --git a/docs/ibc/apps/keeper.md b/docs/ibc/apps/keeper.md new file mode 100644 index 00000000000..6cbba0fbb8f --- /dev/null +++ b/docs/ibc/apps/keeper.md @@ -0,0 +1,88 @@ + + +# Keeper + +Learn how to implement the IBC Module keeper. {synopsis} + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +In the previous sections, on channel handshake callbacks and port binding in `InitGenesis`, a reference was made to keeper methods that need to be implemented when creating a custom IBC module. Below is an overview of how to define an IBC module's keeper. + +> Note that some code has been left out for clarity, to get a full code overview, please refer to [the transfer module's keeper in the ibc-go repo](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/keeper.go). + +```go +// Keeper defines the IBC app module keeper +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryCodec + paramSpace paramtypes.Subspace + + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + scopedKeeper capabilitykeeper.ScopedKeeper + + // ... additional according to custom logic +} + +// NewKeeper creates a new IBC app module Keeper instance +func NewKeeper( + // args +) Keeper { + // ... + + return Keeper{ + cdc: cdc, + storeKey: key, + paramSpace: paramSpace, + + channelKeeper: channelKeeper, + portKeeper: portKeeper, + scopedKeeper: scopedKeeper, + + // ... additional according to custom logic + } +} + +// IsBound checks if the IBC app module is already bound to the desired port +func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok +} + +// BindPort defines a wrapper function for the port Keeper's function in +// order to expose it to module's InitGenesis function +func (k Keeper) BindPort(ctx sdk.Context, portID string) error { + cap := k.portKeeper.BindPort(ctx, portID) + return k.ClaimCapability(ctx, cap, host.PortPath(portID)) +} + +// GetPort returns the portID for the IBC app module. Used in ExportGenesis +func (k Keeper) GetPort(ctx sdk.Context) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(types.PortKey)) +} + +// SetPort sets the portID for the IBC app module. Used in InitGenesis +func (k Keeper) SetPort(ctx sdk.Context, portID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PortKey, []byte(portID)) +} + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.scopedKeeper.AuthenticateCapability(ctx, cap, name) +} + +// ClaimCapability allows the IBC app module to claim a capability that core IBC +// passes to it +func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { + return k.scopedKeeper.ClaimCapability(ctx, cap, name) +} + +// ... additional according to custom logic +``` diff --git a/docs/ibc/apps/packets_acks.md b/docs/ibc/apps/packets_acks.md new file mode 100644 index 00000000000..1871eca8915 --- /dev/null +++ b/docs/ibc/apps/packets_acks.md @@ -0,0 +1,99 @@ + + +# Define packets and acks + +Learn how to define custom packet and acknowledgement structs and how to encode and decode them. {synopsis} + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +## Custom packets + +Modules connected by a channel must agree on what application data they are sending over the +channel, as well as how they will encode/decode it. This process is not specified by IBC as it is up +to each application module to determine how to implement this agreement. However, for most +applications this will happen as a version negotiation during the channel handshake. While more +complex version negotiation is possible to implement inside the channel opening handshake, a very +simple version negotation is implemented in the [ibc-transfer module](https://github.com/cosmos/ibc-go/tree/main/modules/apps/transfer/module.go). + +Thus, a module must define its custom packet data structure, along with a well-defined way to +encode and decode it to and from `[]byte`. + +```go +// Custom packet data defined in application module +type CustomPacketData struct { + // Custom fields ... +} + +EncodePacketData(packetData CustomPacketData) []byte { + // encode packetData to bytes +} + +DecodePacketData(encoded []byte) (CustomPacketData) { + // decode from bytes to packet data +} +``` + +> Note that the `CustomPacketData` struct is defined in the proto definition and then compiled by the protobuf compiler. + +Then a module must encode its packet data before sending it through IBC. + +```go +// Sending custom application packet data +data := EncodePacketData(customPacketData) +packet.Data = data +IBCChannelKeeper.SendPacket(ctx, packet) +``` + +A module receiving a packet must decode the `PacketData` into a structure it expects so that it can +act on it. + +```go +// Receiving custom application packet data (in OnRecvPacket) +packetData := DecodePacketData(packet.Data) +// handle received custom packet data +``` + +## Acknowledgements + +Modules may commit an acknowledgement upon receiving and processing a packet in the case of synchronous packet processing. +In the case where a packet is processed at some later point after the packet has been received (asynchronous execution), the acknowledgement +will be written once the packet has been processed by the application which may be well after the packet receipt. + +NOTE: Most blockchain modules will want to use the synchronous execution model in which the module processes and writes the acknowledgement +for a packet as soon as it has been received from the IBC module. + +This acknowledgement can then be relayed back to the original sender chain, which can take action +depending on the contents of the acknowledgement. + +Just as packet data was opaque to IBC, acknowledgements are similarly opaque. Modules must pass and +receive acknowledegments with the IBC modules as byte strings. + +Thus, modules must agree on how to encode/decode acknowledgements. The process of creating an +acknowledgement struct along with encoding and decoding it, is very similar to the packet data +example above. [ICS 04](https://github.com/cosmos/ibc/blob/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope) +specifies a recommended format for acknowledgements. This acknowledgement type can be imported from +[channel types](https://github.com/cosmos/ibc-go/tree/main/modules/core/04-channel/types). + +While modules may choose arbitrary acknowledgement structs, a default acknowledgement types is provided by IBC [here](https://github.com/cosmos/ibc-go/blob/main/proto/ibc/core/channel/v1/channel.proto): + +```protobuf +// Acknowledgement is the recommended acknowledgement format to be used by +// app-specific protocols. +// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +// conflicts with other protobuf message formats used for acknowledgements. +// The first byte of any message with this format will be the non-ASCII values +// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +// https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope +message Acknowledgement { + // response contains either a result or an error and must be non-empty + oneof response { + bytes result = 21; + string error = 22; + } +} +``` diff --git a/docs/ibc/apps/routing.md b/docs/ibc/apps/routing.md new file mode 100644 index 00000000000..1095462dcba --- /dev/null +++ b/docs/ibc/apps/routing.md @@ -0,0 +1,36 @@ + + +# Routing + +## Pre-requisites Readings + +- [IBC Overview](../overview.md)) {prereq} +- [IBC default integration](../integration.md) {prereq} + +Learn how to hook a route to the IBC router for the custom IBC module. {synopsis} + +As mentioned above, modules must implement the `IBCModule` interface (which contains both channel +handshake callbacks and packet handling callbacks). The concrete implementation of this interface +must be registered with the module name as a route on the IBC `Router`. + +```go +// app.go +func NewApp(...args) *App { +// ... + +// Create static IBC router, add module routes, then set and seal it +ibcRouter := port.NewRouter() + +ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) +// Note: moduleCallbacks must implement IBCModule interface +ibcRouter.AddRoute(moduleName, moduleCallbacks) + +// Setting Router will finalize all routes by sealing router +// No more routes can be added +app.IBCKeeper.SetRouter(ibcRouter) + +// ... +} +``` diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index 09c1d2d2de9..64c7ead5d89 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -24,7 +24,7 @@ Integrating the IBC module to your SDK-based application is straighforward. The ### Module `BasicManager` and `ModuleAccount` permissions -The first step is to add the following modules to the `BasicManager`: `x/capability`, `x/ibc`, +The first step is to add the following modules to the `BasicManager`: `x/capability`, `x/ibc`, and `x/ibc-transfer`. After that, we need to grant `Minter` and `Burner` permissions to the `ibc-transfer` `ModuleAccount` to mint and burn relayed tokens. @@ -72,7 +72,7 @@ type App struct { ### Configure the `Keepers` -During initialization, besides initializing the IBC `Keepers` (for the `x/ibc`, and +During initialization, besides initializing the IBC `Keepers` (for the `x/ibc`, and `x/ibc-transfer` modules), we need to grant specific capabilities through the capability module `ScopedKeepers` so that we can authenticate the object-capability permissions for each of the IBC channels. @@ -139,7 +139,7 @@ func NewApp(...args) *App { ### Module Managers -In order to use IBC, we need to add the new modules to the module `Manager` and to the `SimulationManager` in case your application supports [simulations](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/simulator.md). +In order to use IBC, we need to add the new modules to the module `Manager` and to the `SimulationManager` in case your application supports [simulations](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules/14-simulator.md). ```go // app.go @@ -176,10 +176,7 @@ past historical info at any given height in order to verify the light client `Co connection handhake. The IBC module also has -[`BeginBlock`](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/abci.go) logic as -well. This is optional as it is only required if your application uses the [localhost -client](https://github.com/cosmos/ibc/blob/master/spec/client/ics-009-loopback-client) to connect two -different modules from the same chain. +[`BeginBlock`](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/abci.go) logic as well. This is optional as it is only required if your application uses the localhost client to connect two different modules from the same chain. ::: tip Only register the ibc module to the `SetOrderBeginBlockers` if your application will use the @@ -221,4 +218,4 @@ different chains. If you want to have a broader view of the changes take a look ## Next {hide} -Learn about how to create [custom IBC modules](./apps.md) for your application {hide} +Learn about how to create [custom IBC modules](./apps/apps.md) for your application {hide} diff --git a/docs/ibc/middleware/develop.md b/docs/ibc/middleware/develop.md index 705040b1db7..3c0c74a0186 100644 --- a/docs/ibc/middleware/develop.md +++ b/docs/ibc/middleware/develop.md @@ -2,7 +2,7 @@ order: 1 --> -# IBC Middleware +# IBC middleware Learn how to write your own custom middleware to wrap an IBC application, and understand how to hook different middleware to IBC base applications to form different IBC application stacks {synopsis}. @@ -12,11 +12,11 @@ IBC applications are designed to be self-contained modules that implement their Middleware allows developers to define the extensions as separate modules that can wrap over the base application. This middleware can thus perform its own custom logic, and pass data into the application so that it may run its logic without being aware of the middleware's existence. This allows both the application and the middleware to implement its own isolated logic while still being able to run as part of a single packet flow. -## Pre-requisite Readings +## Pre-requisite readings - [IBC Overview](../overview.md) {prereq} - [IBC Integration](../integration.md) {prereq} -- [IBC Application Developer Guide](../apps.md) {prereq} +- [IBC Application Developer Guide](../apps/apps.md) {prereq} ## Definitions @@ -26,11 +26,11 @@ Middleware allows developers to define the extensions as separate modules that c `Base Application`: A base application is an IBC application that does not contain any middleware. It may be nested by 0 or multiple middleware to form an application stack. -`Application Stack (or stack)`: A stack is the complete set of application logic (middleware(s) + base application) that gets connected to core IBC. A stack may be just a base application, or it may be a series of middlewares that nest a base application. +`Application Stack (or stack)`: A stack is the complete set of application logic (middleware(s) + base application) that gets connected to core IBC. A stack may be just a base application, or it may be a series of middlewares that nest a base application. -## Create a custom IBC Middleware +## Create a custom IBC middleware -IBC Middleware will wrap over an underlying IBC application and sits between core IBC and the application. It has complete control in modifying any message coming from IBC to the application, and any message coming from the application to core IBC. Thus, middleware must be completely trusted by chain developers who wish to integrate them, however this gives them complete flexibility in modifying the application(s) they wrap. +IBC middleware will wrap over an underlying IBC application and sits between core IBC and the application. It has complete control in modifying any message coming from IBC to the application, and any message coming from the application to core IBC. Thus, middleware must be completely trusted by chain developers who wish to integrate them, however this gives them complete flexibility in modifying the application(s) they wrap. #### Interfaces @@ -48,22 +48,31 @@ type Middleware interface { // which will call the next middleware until it reaches the core IBC handler. type ICS4Wrapper interface { SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet) error - WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet, ack []byte) error - GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) + WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet, ack exported.Acknowledgement) error + GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) } ``` ### Implement `IBCModule` interface and callbacks -The IBCModule is struct that implements the ICS26Interface (`porttypes.IBCModule`). It is recommended to separate these callbacks into a separate file `ibc_module.go`. As will be mentioned in the [integration doc](./integration.md), this struct should be different than the struct that implements `AppModule` in case the middleware maintains its own internal state and processes separate SDK messages. +The `IBCModule` is a struct that implements the [ICS-26 interface (`porttypes.IBCModule`)](https://github.com/cosmos/ibc-go/blob/main/modules/core/05-port/types/module.go#L11-L106). It is recommended to separate these callbacks into a separate file `ibc_module.go`. As will be mentioned in the [integration section](./integration.md), this struct should be different than the struct that implements `AppModule` in case the middleware maintains its own internal state and processes separate SDK messages. The middleware must have access to the underlying application, and be called before during all ICS-26 callbacks. It may execute custom logic during these callbacks, and then call the underlying application's callback. Middleware **may** choose not to call the underlying application's callback at all. Though these should generally be limited to error cases. -In the case where the IBC middleware expects to speak to a compatible IBC middleware on the counterparty chain; they must use the channel handshake to negotiate the middleware version without interfering in the version negotiation of the underlying application. +In the case where the IBC middleware expects to speak to a compatible IBC middleware on the counterparty chain, they must use the channel handshake to negotiate the middleware version without interfering in the version negotiation of the underlying application. -Middleware accomplishes this by formatting the version in the following format: `{mw-version}:{app-version}`. +Middleware accomplishes this by formatting the version in a JSON-encoded string containing the middleware version and the application version. The application version may as well be a JSON-encoded string, possibly including further middleware and app versions, if the application stack consists of multiple milddlewares wrapping a base application. The format of the version is specified in ICS-30 as the following: -During the handshake callbacks, the middleware can split the version into: `mw-version`, `app-version`. It can do its negotiation logic on `mw-version`, and pass the `app-version` to the underlying application. +```json +{ + "": "", + "app_version": "" +} +``` + +The `` key in the JSON struct should be replaced by the actual name of the key for the corresponding middleware (e.g. `fee_version`). + +During the handshake callbacks, the middleware can unmarshal the version string and retrieve the middleware and application versions. It can do its negotiation logic on ``, and pass the `` to the underlying application. The middleware should simply pass the capability in the callback arguments along to the underlying application so that it may be claimed by the base application. The base application will then pass the capability up the stack in order to authenticate an outgoing packet/acknowledgement. @@ -71,8 +80,11 @@ In the case where the middleware wishes to send a packet or acknowledgment witho ### Handshake callbacks +#### `OnChanOpenInit` + ```go -func (im IBCModule) OnChanOpenInit(ctx sdk.Context, +func (im IBCModule) OnChanOpenInit( + ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, @@ -80,11 +92,41 @@ func (im IBCModule) OnChanOpenInit(ctx sdk.Context, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string, -) error { - // core/04-channel/types contains a helper function to split middleware and underlying app version - middlewareVersion, appVersion = channeltypes.SplitChannelVersion(version) +) (string, error) { + if version != "" { + // try to unmarshal JSON-encoded version string and pass + // the app-specific version to app callback. + // otherwise, pass version directly to app callback. + metadata, err := Unmarshal(version) + if err != nil { + // Since it is valid for fee version to not be specified, + // the above middleware version may be for another middleware. + // Pass the entire version string onto the underlying application. + return im.app.OnChanOpenInit( + ctx, + order, + connectionHops, + portID, + channelID, + channelCap, + counterparty, + version, + ) + } + else { + metadata = { + // set middleware version to default value + MiddlewareVersion: defaultMiddlewareVersion, + // allow application to return its default version + AppVersion: "", + } + } + doCustomLogic() - im.app.OnChanOpenInit( + + // if the version string is empty, OnChanOpenInit is expected to return + // a default version string representing the version(s) it supports + appVersion, err := im.app.OnChanOpenInit( ctx, order, connectionHops, @@ -92,10 +134,23 @@ func (im IBCModule) OnChanOpenInit(ctx sdk.Context, channelID, channelCap, counterparty, - appVersion, // note we only pass app version here + metadata.AppVersion, // note we only pass app version here ) + if err != nil { + return "", err + } + + version := constructVersion(metadata.MiddlewareVersion, appVersion) + + return version, nil } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L34-L82) an example implementation of this callback for the ICS29 Fee Middleware module. +#### `OnChanOpenTry` + +```go func OnChanOpenTry( ctx sdk.Context, order channeltypes.Order, @@ -106,12 +161,27 @@ func OnChanOpenTry( counterparty channeltypes.Counterparty, counterpartyVersion string, ) (string, error) { - doCustomLogic() + // try to unmarshal JSON-encoded version string and pass + // the app-specific version to app callback. + // otherwise, pass version directly to app callback. + cpMetadata, err := Unmarshal(counterpartyVersion) + if err != nil { + return app.OnChanOpenTry( + ctx, + order, + connectionHops, + portID, + channelID, + channelCap, + counterparty, + counterpartyVersion, + ) + } - // core/04-channel/types contains a helper function to split middleware and underlying app version - cpMiddlewareVersion, cpAppVersion = channeltypes.SplitChannelVersion(counterpartyVersion) + doCustomLogic() - // call the underlying applications OnChanOpenTry callback + // Call the underlying application's OnChanOpenTry callback. + // The try callback must select the final app-specific version string and return it. appVersion, err := app.OnChanOpenTry( ctx, order, @@ -120,35 +190,55 @@ func OnChanOpenTry( channelID, channelCap, counterparty, - cpAppVersion, // note we only pass counterparty app version here + cpMetadata.AppVersion, // note we only pass counterparty app version here ) if err != nil { - return err + return "", err } - - middlewareVersion := negotiateMiddlewareVersion(cpMiddlewareVersion) + + // negotiate final middleware version + middlewareVersion := negotiateMiddlewareVersion(cpMetadata.MiddlewareVersion) version := constructVersion(middlewareVersion, appVersion) - return version + return version, nil } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L84-L124) an example implementation of this callback for the ICS29 Fee Middleware module. + +#### `OnChanOpenAck` +```go func OnChanOpenAck( ctx sdk.Context, portID, channelID string, + counterpartyChannelID string, counterpartyVersion string, ) error { - // core/04-channel/types contains a helper function to split middleware and underlying app version - middlewareVersion, appVersion = channeltypes.SplitChannelVersion(version) - if !isCompatible(middlewareVersion) { + // try to unmarshal JSON-encoded version string and pass + // the app-specific version to app callback. + // otherwise, pass version directly to app callback. + cpMetadata, err = UnmarshalJSON(counterpartyVersion) + if err != nil { + return app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + } + + if !isCompatible(cpMetadata.MiddlewareVersion) { return error } doCustomLogic() - - // call the underlying applications OnChanOpenTry callback - app.OnChanOpenAck(ctx, portID, channelID, appVersion) + + // call the underlying application's OnChanOpenTry callback + return app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, cpMetadata.AppVersion) } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L126-L152) an example implementation of this callback for the ICS29 Fee Middleware module. +### `OnChanOpenConfirm` + +```go func OnChanOpenConfirm( ctx sdk.Context, portID, @@ -156,93 +246,151 @@ func OnChanOpenConfirm( ) error { doCustomLogic() - app.OnChanOpenConfirm(ctx, portID, channelID) + return app.OnChanOpenConfirm(ctx, portID, channelID) } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L154-L162) an example implementation of this callback for the ICS29 Fee Middleware module. -OnChanCloseInit( +#### `OnChanCloseInit` + +```go +func OnChanCloseInit( ctx sdk.Context, portID, channelID string, ) error { doCustomLogic() - app.OnChanCloseInit(ctx, portID, channelID) + return app.OnChanCloseInit(ctx, portID, channelID) } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L164-L187) an example implementation of this callback for the ICS29 Fee Middleware module. + +#### `OnChanCloseConfirm` -OnChanCloseConfirm( +```go +func OnChanCloseConfirm( ctx sdk.Context, portID, channelID string, ) error { doCustomLogic() - app.OnChanCloseConfirm(ctx, portID, channelID) + return app.OnChanCloseConfirm(ctx, portID, channelID) } ``` -NOTE: Middleware that does not need to negotiate with a counterparty middleware on the remote stack will not implement the version splitting and negotiation, and will simply perform its own custom logic on the callbacks without relying on the counterparty behaving similarly. +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L189-L212) an example implementation of this callback for the ICS29 Fee Middleware module. + +**NOTE**: Middleware that does not need to negotiate with a counterparty middleware on the remote stack will not implement the version unmarshalling and negotiation, and will simply perform its own custom logic on the callbacks without relying on the counterparty behaving similarly. ### Packet callbacks The packet callbacks just like the handshake callbacks wrap the application's packet callbacks. The packet callbacks are where the middleware performs most of its custom logic. The middleware may read the packet flow data and perform some additional packet handling, or it may modify the incoming data before it reaches the underlying application. This enables a wide degree of usecases, as a simple base application like token-transfer can be transformed for a variety of usecases by combining it with custom middleware. +#### `OnRecvPacket` + ```go -OnRecvPacket( +func OnRecvPacket( ctx sdk.Context, packet channeltypes.Packet, + relayer sdk.AccAddress, ) ibcexported.Acknowledgement { doCustomLogic(packet) - ack := app.OnRecvPacket(ctx, packet) + ack := app.OnRecvPacket(ctx, packet, relayer) doCustomLogic(ack) // middleware may modify outgoing ack return ack } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L214-L237) an example implementation of this callback for the ICS29 Fee Middleware module. -OnAcknowledgementPacket( +#### `OnAcknowledgementPacket` + +```go +func OnAcknowledgementPacket( ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, -) (*sdk.Result, error) { + relayer sdk.AccAddress, +) error { doCustomLogic(packet, ack) - app.OnAcknowledgementPacket(ctx, packet, ack) + return app.OnAcknowledgementPacket(ctx, packet, ack, relayer) } +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L239-L292) an example implementation of this callback for the ICS29 Fee Middleware module. -OnTimeoutPacket( +#### `OnTimeoutPacket` + +```go +func OnTimeoutPacket( ctx sdk.Context, packet channeltypes.Packet, -) (*sdk.Result, error) { + relayer sdk.AccAddress, +) error { doCustomLogic(packet) - app.OnTimeoutPacket(ctx, packet) + return app.OnTimeoutPacket(ctx, packet, relayer) +} +``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L294-L334) an example implementation of this callback for the ICS29 Fee Middleware module. + +### ICS-4 wrappers + +Middleware must also wrap ICS-4 so that any communication from the application to the `channelKeeper` goes through the middleware first. Similar to the packet callbacks, the middleware may modify outgoing acknowledgements and packets in any way it wishes. + +#### `SendPacket` + +```go +func SendPacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + appPacket exported.PacketI, +) { + // middleware may modify packet + packet = doCustomLogic(appPacket) + + return ics4Keeper.SendPacket(ctx, chanCap, packet) } ``` -### ICS-4 Wrappers +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L336-L343) an example implementation of this function for the ICS29 Fee Middleware module. -Middleware must also wrap ICS-4 so that any communication from the application to the channelKeeper goes through the middleware first. Similar to the packet callbacks, the middleware may modify outgoing acknowledgements and packets in any way it wishes. +#### `WriteAcknowledgement` ```go // only called for async acks func WriteAcknowledgement( - packet channeltypes.Packet, - acknowledgement []bytes) { + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, +) { // middleware may modify acknowledgement - ack_bytes = doCustomLogic(acknowledgement) + ack_bytes = doCustomLogic(ack) return ics4Keeper.WriteAcknowledgement(packet, ack_bytes) } +``` -func SendPacket(appPacket channeltypes.Packet) { - // middleware may modify packet - packet = doCustomLogic(app_packet) +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L345-L353) an example implementation of this function for the ICS29 Fee Middleware module. - return ics4Keeper.SendPacket(packet) -} +#### `GetAppVersion` -// middleware must return the underlying application version -func GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { +```go +// middleware must return the underlying application version +func GetAppVersion( + ctx sdk.Context, + portID, + channelID string, +) (string, bool) { version, found := ics4Keeper.GetAppVersion(ctx, portID, channelID) if !found { return "", false @@ -261,3 +409,5 @@ func GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { return metadata.AppVersion, true } ``` + +See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L355-L358) an example implementation of this function for the ICS29 Fee Middleware module. diff --git a/docs/ibc/middleware/integration.md b/docs/ibc/middleware/integration.md index 68a8de00899..12eb447f8ea 100644 --- a/docs/ibc/middleware/integration.md +++ b/docs/ibc/middleware/integration.md @@ -2,7 +2,7 @@ order: 2 --> -# Integrating IBC Middleware into a Chain +# Integrating IBC middleware into a chain Learn how to integrate IBC middleware(s) with a base application to your chain. The following document only applies for Cosmos SDK chains. @@ -46,18 +46,18 @@ scopedKeeperCustom2 := capabilityKeeper.NewScopedKeeper("custom2") // initialize base IBC applications // if you want to create two different stacks with the same base application, // they must be given different scopedKeepers and assigned different ports. -transferIBCModule := transfer.NewIBCModule(transferKeeper, scopedKeeperTransfer) -customIBCModule1 := custom.NewIBCModule(customKeeper, scopedKeeperCustom1, "portCustom1") -customIBCModule2 := custom.NewIBCModule(customKeeper, scopedKeeperCustom2, "portCustom2") +transferIBCModule := transfer.NewIBCModule(transferKeeper) +customIBCModule1 := custom.NewIBCModule(customKeeper, "portCustom1") +customIBCModule2 := custom.NewIBCModule(customKeeper, "portCustom2") // create IBC stacks by combining middleware with base application // NOTE: since middleware2 is stateless it does not require a Keeper // stack 1 contains mw1 -> mw3 -> transfer -stack1 := mw1.NewIBCModule(mw1Keeper, mw3.NewIBCModule(mw3Keeper, transferIBCModule)) +stack1 := mw1.NewIBCMiddleware(mw3.NewIBCMiddleware(transferIBCModule, mw3Keeper), mw1Keeper) // stack 2 contains mw3 -> mw2 -> custom1 -stack2 := mw3.NewIBCModule(mw3Keeper, mw3.NewIBCModule(customIBCModule1)) +stack2 := mw3.NewIBCMiddleware(mw2.NewIBCMiddleware(customIBCModule1), mw3Keeper) // stack 3 contains mw2 -> mw1 -> custom2 -stack3 := mw2.NewIBCModule(mw1.NewIBCModule(mw1Keeper, customIBCModule2)) +stack3 := mw2.NewIBCMiddleware(mw1.NewIBCMiddleware(customIBCModule2, mw1Keeper)) // associate each stack with the moduleName provided by the underlying scopedKeeper ibcRouter := porttypes.NewRouter() diff --git a/docs/ibc/overview.md b/docs/ibc/overview.md index f36b366a5e0..588580f0931 100644 --- a/docs/ibc/overview.md +++ b/docs/ibc/overview.md @@ -136,7 +136,7 @@ Proofs are passed from core IBC to light-clients as bytes. It is up to light cli [ICS-24 Host State Machine Requirements](https://github.com/cosmos/ics/tree/master/spec/core/ics-024-host-requirements). - The proof format that all implementations must be able to produce and verify is defined in [ICS-23 Proofs](https://github.com/confio/ics23) implementation. -### [Capabilities](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/ocap.md) +### [Capabilities](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/core/10-ocap.md) IBC is intended to work in execution environments where modules do not necessarily trust each other. Thus, IBC must authenticate module actions on ports and channels so that only modules with the diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 30c6d350bd1..c7f1b1e9652 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -84,6 +84,8 @@ - [Params](#ibc.applications.interchain_accounts.controller.v1.Params) - [ibc/applications/interchain_accounts/controller/v1/query.proto](#ibc/applications/interchain_accounts/controller/v1/query.proto) + - [QueryInterchainAccountRequest](#ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountRequest) + - [QueryInterchainAccountResponse](#ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountResponse) - [QueryParamsRequest](#ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest) - [QueryParamsResponse](#ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse) @@ -1460,6 +1462,37 @@ The following parameters may be used to disable the controller submodule. + + +### QueryInterchainAccountRequest +QueryInterchainAccountRequest is the request type for the Query/InterchainAccount RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `owner` | [string](#string) | | | +| `connection_id` | [string](#string) | | | + + + + + + + + +### QueryInterchainAccountResponse +QueryInterchainAccountResponse the response type for the Query/InterchainAccount RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | | + + + + + + ### QueryParamsRequest @@ -1498,6 +1531,7 @@ Query provides defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `InterchainAccount` | [QueryInterchainAccountRequest](#ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountRequest) | [QueryInterchainAccountResponse](#ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountResponse) | InterchainAccount returns the interchain account address for a given owner address on a given connection | GET|/ibc/apps/interchain_accounts/controller/v1/owners/{owner}/connections/{connection_id}| | `Params` | [QueryParamsRequest](#ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse) | Params queries all parameters of the ICA controller submodule. | GET|/ibc/apps/interchain_accounts/controller/v1/params| @@ -2112,6 +2146,7 @@ https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transf | `receiver` | [string](#string) | | the recipient address on the destination chain | | `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Timeout height relative to the current block height. The timeout is disabled when set to 0. | | `timeout_timestamp` | [uint64](#uint64) | | Timeout timestamp in absolute nanoseconds since unix epoch. The timeout is disabled when set to 0. | +| `memo` | [string](#string) | | optional memo | @@ -2124,6 +2159,11 @@ https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transf MsgTransferResponse defines the Msg/Transfer response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | sequence number of the transfer packet sent | + + @@ -2168,6 +2208,7 @@ https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transf | `amount` | [string](#string) | | the token amount to be transferred | | `sender` | [string](#string) | | the sender address | | `receiver` | [string](#string) | | the recipient address on the destination chain | +| `memo` | [string](#string) | | optional memo | @@ -2949,7 +2990,7 @@ value will be ignored by core IBC. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `port_id` | [string](#string) | | | -| `previous_channel_id` | [string](#string) | | in the case of crossing hello's, when both chains call OpenInit, we need the channel identifier of the previous channel in state INIT | +| `previous_channel_id` | [string](#string) | | **Deprecated.** Deprecated: this field is unused. Crossing hello's are no longer supported in core IBC. | | `channel` | [Channel](#ibc.core.channel.v1.Channel) | | NOTE: the version field within the channel has been deprecated. Its value will be ignored by core IBC. | | `counterparty_version` | [string](#string) | | | | `proof_init` | [bytes](#bytes) | | | @@ -4244,7 +4285,7 @@ connection on Chain B. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `client_id` | [string](#string) | | | -| `previous_connection_id` | [string](#string) | | in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier of the previous connection in state INIT | +| `previous_connection_id` | [string](#string) | | **Deprecated.** Deprecated: this field is unused. Crossing hellos are no longer supported in core IBC. | | `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | | `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | | | `delay_period` | [uint64](#uint64) | | | diff --git a/docs/ibc/relayer.md b/docs/ibc/relayer.md index ce3fabe252d..8327ebfb8bf 100644 --- a/docs/ibc/relayer.md +++ b/docs/ibc/relayer.md @@ -7,7 +7,7 @@ order: 6 ## Pre-requisites Readings - [IBC Overview](./overview.md) {prereq} -- [Events](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/events.md) {prereq} +- [Events](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/core/08-events.md) {prereq} ## Events @@ -27,7 +27,7 @@ a module event emission with the attribute value `ibc_` (02-clien ### Subscribing with Tendermint -Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/v0.35/rpc/) will return events using +Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/main/rpc/) will return events using Tendermint's internal representation of them. Instead of receiving back a list of events as they were emitted, Tendermint will return the type `map[string][]string` which maps a string in the form `.` to `attribute_value`. This causes extraction of the event @@ -42,5 +42,5 @@ piece of information needed to relay a packet. ## Example Implementations -- [Golang Relayer](https://github.com/iqlusioninc/relayer) -- [Hermes](https://github.com/informalsystems/ibc-rs/tree/master/relayer) +- [Golang Relayer](https://github.com/cosmos/relayer) +- [Hermes](https://github.com/informalsystems/ibc-rs/tree/master/crates/relayer) diff --git a/docs/ibc/upgrades/developer-guide.md b/docs/ibc/upgrades/developer-guide.md index d41b3346d4f..73a19b93664 100644 --- a/docs/ibc/upgrades/developer-guide.md +++ b/docs/ibc/upgrades/developer-guide.md @@ -36,7 +36,7 @@ Developers must ensure that the new client adopts all of the new Client paramete Upgrades must adhere to the IBC Security Model. IBC does not rely on the assumption of honest relayers for correctness. Thus users should not have to rely on relayers to maintain client correctness and security (though honest relayers must exist to maintain relayer liveness). While relayers may choose any set of client parameters while creating a new `ClientState`, this still holds under the security model since users can always choose a relayer-created client that suits their security and correctness needs or create a Client with their desired parameters if no such client exists. -However, when upgrading an existing client, one must keep in mind that there are already many users who depend on this client's particular parameters. We cannot give the upgrading relayer free choice over these parameters once they have already been chosen. This would violate the security model since users who rely on the client would have to rely on the upgrading relayer to maintain the same level of security. Thus, developers must make sure that their upgrade mechanism allows clients to upgrade the chain-specified parameters whenever a chain upgrade changes these parameters (examples in the Tendermint client include `UnbondingPeriod`, `ChainID`, `UpgradePath`, etc.), while ensuring that the relayer submitting the `UpgradeClientMsg` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustingPeriod`, `TrustLevel`, `MaxClockDrift`, etc). +However, when upgrading an existing client, one must keep in mind that there are already many users who depend on this client's particular parameters. We cannot give the upgrading relayer free choice over these parameters once they have already been chosen. This would violate the security model since users who rely on the client would have to rely on the upgrading relayer to maintain the same level of security. Thus, developers must make sure that their upgrade mechanism allows clients to upgrade the chain-specified parameters whenever a chain upgrade changes these parameters (examples in the Tendermint client include `UnbondingPeriod`, `TrustingPeriod`, `ChainID`, `UpgradePath`, etc.), while ensuring that the relayer submitting the `UpgradeClientMsg` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustLevel`, `MaxClockDrift`, etc). Developers should maintain the distinction between Client parameters that are uniform across every valid light client of a chain (chain-chosen parameters), and Client parameters that are customizable by each individual client (client-chosen parameters); since this distinction is necessary to implement the `ZeroCustomFields` method in the `ClientState` interface: diff --git a/docs/middleware/ics29-fee/end-users.md b/docs/middleware/ics29-fee/end-users.md index 08857d21a37..0a2514e7722 100644 --- a/docs/middleware/ics29-fee/end-users.md +++ b/docs/middleware/ics29-fee/end-users.md @@ -8,7 +8,7 @@ Learn how to incentivize IBC packets using the ICS29 Fee Middleware module. {syn ## Pre-requisite readings -* [Fee Middleware](overview.md) {prereq} +- [Fee Middleware](overview.md) {prereq} ## Summary @@ -17,7 +17,6 @@ Different types of end users: - CLI users who want to manually incentivize IBC packets - Client developers - The Fee Middleware module allows end users to add a 'tip' to each IBC packet which will incentivize relayer operators to relay packets between chains. gRPC endpoints are exposed for client developers as well as a simple CLI for manually incentivizing IBC packets. ## CLI Users @@ -26,6 +25,6 @@ For an in depth guide on how to use the ICS29 Fee Middleware module using the CL ## Client developers -Client developers can read more about the relevant ICS29 message types in the [Escrowing and paying out fees section](../ics29-fee/msgs.md). +Client developers can read more about the relevant ICS29 message types in the [Fee messages section](../ics29-fee/msgs.md). [CosmJS](https://github.com/cosmos/cosmjs) is a useful client library for signing and broadcasting Cosmos SDK messages. diff --git a/docs/middleware/ics29-fee/events.md b/docs/middleware/ics29-fee/events.md index 04ab5b34cee..e0d2ccd471c 100644 --- a/docs/middleware/ics29-fee/events.md +++ b/docs/middleware/ics29-fee/events.md @@ -4,10 +4,12 @@ order: 5 # Events +An overview of all events related to ICS-29 {synopsis} + ## `MsgPayPacketFee`, `MsgPayPacketFeeAsync` | Type | Attribute Key | Attribute Value | -|-------------------------|-----------------|-----------------| +| ----------------------- | --------------- | --------------- | | incentivized_ibc_packet | port_id | {portID} | | incentivized_ibc_packet | channel_id | {channelID} | | incentivized_ibc_packet | packet_sequence | {sequence} | @@ -19,16 +21,16 @@ order: 5 ## `RegisterPayee` | Type | Attribute Key | Attribute Value | -|----------------|------------|--------------------| -| register_payee | relayer | {relayer} | -| register_payee | payee | {payee} | -| register_payee | channel_id | {channelID} | -| message | module | fee-ibc | +| -------------- | ------------- | --------------- | +| register_payee | relayer | {relayer} | +| register_payee | payee | {payee} | +| register_payee | channel_id | {channelID} | +| message | module | fee-ibc | ## `RegisterCounterpartyPayee` | Type | Attribute Key | Attribute Value | -|-----------------------------|--------------------|---------------------| +| --------------------------- | ------------------ | ------------------- | | register_counterparty_payee | relayer | {relayer} | | register_counterparty_payee | counterparty_payee | {counterpartyPayee} | | register_counterparty_payee | channel_id | {channelID} | diff --git a/docs/middleware/ics29-fee/fee-distribution.md b/docs/middleware/ics29-fee/fee-distribution.md index 9cf3a821a21..5200fdaaf53 100644 --- a/docs/middleware/ics29-fee/fee-distribution.md +++ b/docs/middleware/ics29-fee/fee-distribution.md @@ -8,7 +8,7 @@ Learn about payee registration for the distribution of packet fees. The followin ## Pre-requisite readings -* [Fee Middleware](overview.md) {prereq} +- [Fee Middleware](overview.md) {prereq} Packet fees are divided into 3 distinct amounts in order to compensate relayer operators for packet relaying on fee enabled IBC channels. @@ -16,18 +16,22 @@ Packet fees are divided into 3 distinct amounts in order to compensate relayer o - `AckFee`: The sum of all packet acknowledgement fees distributed to a payee for successful execution of `MsgAcknowledgement`. - `TimeoutFee`: The sum of all packet timeout fees distributed to a payee for successful execution of `MsgTimeout`. -## Register a payee address for forward relaying +## Register a counterparty payee address for forward relaying + +As mentioned in [ICS29 Concepts](../ics29-fee/overview.md#concepts), the forward relayer describes the actor who performs the submission of `MsgRecvPacket` on the destination chain. +Fee distribution for incentivized packet relays takes place on the packet source chain. + +> Relayer operators are expected to register a counterparty payee address, in order to be compensated accordingly with `RecvFee`s upon completion of a packet lifecycle. -As mentioned in [ICS29 Concepts](../ics29-fee/overview.md#concepts), the forward relayer describes the actor who performs the submission of `MsgRecvPacket` on the destination chain. -Fee distribution for incentivized packet relays takes place on the packet source chain. -Relayer operators are expected to register a counterparty payee address, in order to be compensated accordingly with `RecvFee`s upon completion of a packet lifecycle. The counterparty payee address registered on the destination chain is encoded into the packet acknowledgement and communicated as such to the source chain for fee distribution. -If a counterparty payee is not registered for the forward relayer on the destination chain, the escrowed fees will be refunded upon fee distribution. +**If a counterparty payee is not registered for the forward relayer on the destination chain, the escrowed fees will be refunded upon fee distribution.** + +### Relayer operator actions? -A transaction must be submitted to the destination chain including a `CounterpartyPayee` address of an account on the source chain. +A transaction must be submitted **to the destination chain** including a `CounterpartyPayee` address of an account on the source chain. The transaction must be signed by the `Relayer`. -Note: If a module account address is used as the `CounterpartyPayee` it is recommended to [turn off invariant checks](https://github.com/cosmos/ibc-go/blob/71d7480c923f4227453e8a80f51be01ae7ee845e/testing/simapp/app.go#L659) for that module. +Note: If a module account address is used as the `CounterpartyPayee` but the module has been set as a blocked address in the `BankKeeper`, the refunding to the module account will fail. This is because many modules use invariants to compare internal tracking of module account balances against the actual balance of the account stored in the `BankKeeper`. If a token transfer to the module account occurs without going through this module and updating the account balance of the module on the `BankKeeper`, then invariants may break and unknown behaviour could occur depending on the module implementation. Therefore, if it is desirable to use a module account that is currently blocked, the module developers should be consulted to gauge to possibility of removing the module account from the blocked list. ```go type MsgRegisterCounterpartyPayee struct { @@ -42,27 +46,34 @@ type MsgRegisterCounterpartyPayee struct { } ``` -This message is expected to fail if: - -- `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). -- `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). -- `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/basics/accounts.md#Addresses)). -- `CounterpartyPayee` is empty. +> This message is expected to fail if: +> +> - `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). +> - `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). +> - `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/basics/03-accounts.md#addresses)). +> - `CounterpartyPayee` is empty. See below for an example CLI command: -``` -simd tx ibc-fee register-counterparty-payee transfer channel-0 cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh +```bash +simd tx ibc-fee register-counterparty-payee transfer channel-0 \ +cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ +osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 \ +--from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh ``` -## Register a payee address for reverse and timeout relaying +## Register an alternative payee address for reverse and timeout relaying -As mentioned in [ICS29 Concepts](../ics29-fee/overview.md#concepts), the reverse relayer describes the actor who performs the submission of `MsgAcknowledgement` on the source chain. +As mentioned in [ICS29 Concepts](../ics29-fee/overview.md#concepts), the reverse relayer describes the actor who performs the submission of `MsgAcknowledgement` on the source chain. Similarly the timeout relayer describes the actor who performs the submission of `MsgTimeout` (or `MsgTimeoutOnClose`) on the source chain. -Relayer operators may choose to register an optional payee address, in order to be compensated accordingly with `AckFee`s and `TimeoutFee`s upon completion of a packet life cycle. + +> Relayer operators **may choose** to register an optional payee address, in order to be compensated accordingly with `AckFee`s and `TimeoutFee`s upon completion of a packet life cycle. + If a payee is not registered for the reverse or timeout relayer on the source chain, then fee distribution assumes the default behaviour, where fees are paid out to the relayer account which delivers `MsgAcknowledgement` or `MsgTimeout`/`MsgTimeoutOnClose`. -A transaction must be submitted to the source chain including a `Payee` address of an account on the source chain. +### Relayer operator actions + +A transaction must be submitted **to the source chain** including a `Payee` address of an account on the source chain. The transaction must be signed by the `Relayer`. Note: If a module account address is used as the `Payee` it is recommended to [turn off invariant checks](https://github.com/cosmos/ibc-go/blob/71d7480c923f4227453e8a80f51be01ae7ee845e/testing/simapp/app.go#L659) for that module. @@ -80,15 +91,18 @@ type MsgRegisterPayee struct { } ``` -This message is expected to fail if: - -- `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). -- `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). -- `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/basics/accounts.md#Addresses)). -- `Payee` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/basics/accounts.md#Addresses)). +> This message is expected to fail if: +> +> - `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). +> - `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). +> - `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/basics/03-accounts.md#addresses)). +> - `Payee` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/basics/03-accounts.md#addresses)). See below for an example CLI command: -``` -simd tx ibc-fee register-payee transfer channel-0 cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh cosmos153lf4zntqt33a4v0sm5cytrxyqn78q7kz8j8x5 --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh +```bash +simd tx ibc-fee register-payee transfer channel-0 \ +cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ +cosmos153lf4zntqt33a4v0sm5cytrxyqn78q7kz8j8x5 \ +--from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh ``` diff --git a/docs/middleware/ics29-fee/integration.md b/docs/middleware/ics29-fee/integration.md index f1d0158e702..1a663daa446 100644 --- a/docs/middleware/ics29-fee/integration.md +++ b/docs/middleware/ics29-fee/integration.md @@ -14,6 +14,90 @@ Learn how to configure the Fee Middleware module with IBC applications. The foll The Fee Middleware module, as the name suggests, plays the role of an IBC middleware and as such must be configured by chain developers to route and handle IBC messages correctly. For Cosmos SDK chains this setup is done via the `app/app.go` file, where modules are constructed and configured in order to bootstrap the blockchain application. +## Example integration of the Fee Middleware module + +```go +// app.go + +// Register the AppModule for the fee middleware module +ModuleBasics = module.NewBasicManager( + ... + ibcfee.AppModuleBasic{}, + ... +) + +... + +// Add module account permissions for the fee middleware module +maccPerms = map[string][]string{ + ... + ibcfeetypes.ModuleName: nil, +} + +... + +// Add fee middleware Keeper +type App struct { + ... + + IBCFeeKeeper ibcfeekeeper.Keeper + + ... +} + +... + +// Create store keys +keys := sdk.NewKVStoreKeys( + ... + ibcfeetypes.StoreKey, + ... +) + +... + +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, +) + + +// See the section below for configuring an application stack with the fee middleware module + +... + +// Register fee middleware AppModule +app.moduleManager = module.NewManager( + ... + ibcfee.NewAppModule(app.IBCFeeKeeper), +) + +... + +// Add fee middleware to begin blocker logic +app.moduleManager.SetOrderBeginBlockers( + ... + ibcfeetypes.ModuleName, + ... +) + +// Add fee middleware to end blocker logic +app.moduleManager.SetOrderEndBlockers( + ... + ibcfeetypes.ModuleName, + ... +) + +// Add fee middleware to init genesis logic +app.moduleManager.SetOrderInitGenesis( + ... + ibcfeetypes.ModuleName, + ... +) +``` + ## Configuring an application stack with Fee Middleware As mentioned in [IBC middleware development](../../ibc/middleware/develop.md) an application stack may be composed of many or no middlewares that nest a base application. @@ -82,4 +166,4 @@ ibcRouter. AddRoute(ibcmock.ModuleName+icacontrollertypes.SubModuleName, icaControllerStack) // ica with mock auth module stack route to ica (top level of middleware stack) AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). AddRoute(icahosttypes.SubModuleName, icaHostStack). -``` \ No newline at end of file +``` diff --git a/docs/middleware/ics29-fee/msgs.md b/docs/middleware/ics29-fee/msgs.md index 7ab81cd9552..0003d5982ee 100644 --- a/docs/middleware/ics29-fee/msgs.md +++ b/docs/middleware/ics29-fee/msgs.md @@ -2,71 +2,89 @@ order: 3 --> -# Escrowing fees +# Fee messages + +Learn about the different ways to pay for fees, how the fees are paid out and what happens when not enough escrowed fees are available for payout {synopsis} + +## Escrowing fees The fee middleware module exposes two different ways to pay fees for relaying IBC packets: -1. `MsgPayPacketFee`, which enables the escrowing of fees for a packet at the next sequence send and should be combined into one `MultiMsgTx` with the message that will be paid for. - - Note that the `Relayers` field has been set up to allow for an optional whitelist of relayers permitted to receive this fee, however, this feature has not yet been enabled at this time. - - ``` - type MsgPayPacketFee struct{ - // fee encapsulates the recv, ack and timeout fees associated with an IBC packet - Fee Fee - // the source port unique identifier - SourcePortId string - // the source channel unique identifer - SourceChannelId string - // account address to refund fee if necessary - Signer string - // optional list of relayers permitted to the receive packet fee - Relayers []string - } - ``` - - The `Fee` message contained in this synchronous fee payment method configures different fees which will be paid out for `MsgRecvPacket`, `MsgAcknowledgement`, and `MsgTimeout`/`MsgTimeoutOnClose`. - - ``` - type Fee struct { - RecvFee types.Coins - AckFee types.Coins - TimeoutFee types.Coin` - } - ``` +1. `MsgPayPacketFee`, which enables the escrowing of fees for a packet at the next sequence send and should be combined into one `MultiMsgTx` with the message that will be paid for. + + Note that the `Relayers` field has been set up to allow for an optional whitelist of relayers permitted to receive this fee, however, this feature has not yet been enabled at this time. + + ```go + type MsgPayPacketFee struct{ + // fee encapsulates the recv, ack and timeout fees associated with an IBC packet + Fee Fee + // the source port unique identifier + SourcePortId string + // the source channel unique identifer + SourceChannelId string + // account address to refund fee if necessary + Signer string + // optional list of relayers permitted to the receive packet fee + Relayers []string + } + ``` + + The `Fee` message contained in this synchronous fee payment method configures different fees which will be paid out for `MsgRecvPacket`, `MsgAcknowledgement`, and `MsgTimeout`/`MsgTimeoutOnClose`. + + ```go + type Fee struct { + RecvFee types.Coins + AckFee types.Coins + TimeoutFee types.Coins + } + ``` + +The diagram below shows the `MultiMsgTx` with the `MsgTransfer` coming from a token transfer message, along with `MsgPayPacketFee`. + +![MsgPayPacketFee](../../assets/fee-mw/msgpaypacket.png) 2. `MsgPayPacketFeeAsync`, which enables the asynchronous escrowing of fees for a specified packet: - Note that a packet can be 'topped up' multiple times with additional fees of any coin denomination by broadcasting multiple `MsgPayPacketFeeAsync` messages. - - ``` - type MsgPayPacketFeeAsync struct { - // unique packet identifier comprised of the channel ID, port ID and sequence - PacketId channeltypes.PacketId - // the packet fee associated with a particular IBC packet - PacketFee PacketFee - } - ``` - - where the `PacketFee` also specifies the `Fee` to be paid as well as the refund address for fees which are not paid out - ``` - type PacketFee struct { - Fee Fee - RefundAddress string - Relayers []]string - } - ``` + Note that a packet can be 'topped up' multiple times with additional fees of any coin denomination by broadcasting multiple `MsgPayPacketFeeAsync` messages. + + ```go + type MsgPayPacketFeeAsync struct { + // unique packet identifier comprised of the channel ID, port ID and sequence + PacketId channeltypes.PacketId + // the packet fee associated with a particular IBC packet + PacketFee PacketFee + } + ``` + + where the `PacketFee` also specifies the `Fee` to be paid as well as the refund address for fees which are not paid out + + ```go + type PacketFee struct { + Fee Fee + RefundAddress string + Relayers []string + } + ``` + +The diagram below shows how multiple `MsgPayPacketFeeAsync` can be broadcasted asynchronously. Escrowing of the fee associated with a packet can be carried out by any party because ICS-29 does not dictate a particular fee payer. In fact, chains can choose to simply not expose this fee payment to end users at all and rely on a different module account or even the community pool as the source of relayer incentives. + +![MsgPayPacketFeeAsync](../../assets/fee-mw/paypacketfeeasync.png) Please see our [wiki](https://github.com/cosmos/ibc-go/wiki/Fee-enabled-fungible-token-transfers) for example flows on how to use these messages to incentivise a token transfer channel using a CLI. -# Paying out the escrowed fees - -In the case of a successful transaction, `RecvFee` will be paid out to the designated counterparty payee address which has been registered on the receiver chain and sent back with the `MsgAcknowledgement`, `AckFee` will be paid out to the relayer address which has submitted the `MsgAcknowledgement` on the sending chain (or the registered payee in case one has been registered for the relayer address), and `TimeoutFee` will be reimbursed to the account which escrowed the fee. In cases of timeout transactions, `RecvFee` and `AckFee` will be reimbursed. +## Paying out the escrowed fees + +Following diagram takes a look at the packet flow for an incentivized token transfer and investigates the several scenario's for paying out the escrowed fees. We assume that the relayers have registered their counterparty address, detailed in the [Fee distribution section](../ics29-fee/fee-distribution.md). + +![packet-flow-fee](../../assets/fee-mw/feeflow.png) + +- In the case of a successful transaction, `RecvFee` will be paid out to the designated counterparty payee address which has been registered on the receiver chain and sent back with the `MsgAcknowledgement`, `AckFee` will be paid out to the relayer address which has submitted the `MsgAcknowledgement` on the sending chain (or the registered payee in case one has been registered for the relayer address), and `TimeoutFee` will be reimbursed to the account which escrowed the fee. +- In case of a timeout transaction, `RecvFee` and `AckFee` will be reimbursed. The `TimeoutFee` will be paid to the `Timeout Relayer` (who submits the timeout message to the source chain). -Please note that fee payments are built on the assumption that sender chains are the source of incentives — the chain that sends the packets is the same chain where fee payments will occur -- please see the [relayer operator section](../ics29-fee/fee-distribution.md) to understand the flow for registering payee and counterparty payee (fee receiving) addresses. +> Please note that fee payments are built on the assumption that sender chains are the source of incentives — the chain that sends the packets is the same chain where fee payments will occur -- please see the [Fee distribution section](../ics29-fee/fee-distribution.md) to understand the flow for registering payee and counterparty payee (fee receiving) addresses. -# A locked fee middleware module +## A locked fee middleware module -The fee middleware module can become locked if the situation arises that the escrow account for the fees does not have sufficient funds to pay out the fees which have been escrowed for each packet. This situation indicates a severe bug. In this case, the fee module will be locked until manual intervention fixes the issue. +The fee middleware module can become locked if the situation arises that the escrow account for the fees does not have sufficient funds to pay out the fees which have been escrowed for each packet. _This situation indicates a severe bug._ In this case, the fee module will be locked until manual intervention fixes the issue. -A locked fee module will simply skip fee logic and continue on to the underlying packet flow. A channel with a locked fee module will temporarily function as a fee disabled channel, and the locking of a fee module will not affect the continued flow of packets over the channel. +> A locked fee module will simply skip fee logic and continue on to the underlying packet flow. A channel with a locked fee module will temporarily function as a fee disabled channel, and the locking of a fee module will not affect the continued flow of packets over the channel. diff --git a/docs/middleware/ics29-fee/overview.md b/docs/middleware/ics29-fee/overview.md index e23261c712e..bf1e9ba6dc4 100644 --- a/docs/middleware/ics29-fee/overview.md +++ b/docs/middleware/ics29-fee/overview.md @@ -8,17 +8,29 @@ Learn about what the Fee Middleware module is, and how to build custom modules t ## What is the Fee Middleware module? -IBC does not depend on relayer operators for transaction verification. However, the relayer infrastructure ensures liveness of the Interchain network — operators listen for packets sent through channels opened between chains, and perform the vital service of ferrying these packets (and proof of the transaction on the sending chain/receipt on the receiving chain) to the clients on each side of the channel. +IBC does not depend on relayer operators for transaction verification. However, the relayer infrastructure ensures liveness of the Interchain network — operators listen for packets sent through channels opened between chains, and perform the vital service of ferrying these packets (and proof of the transaction on the sending chain/receipt on the receiving chain) to the clients on each side of the channel. -Though relaying is permissionless and completely decentralized and accessible, it does come with operational costs. Running full nodes to query transaction proofs and paying for transaction fees associated with IBC packets are two of the primary cost burdens which have driven the overall discussion on a general, in-protocol incentivization mechanism for relayers. +Though relaying is permissionless and completely decentralized and accessible, it does come with operational costs. Running full nodes to query transaction proofs and paying for transaction fees associated with IBC packets are two of the primary cost burdens which have driven the overall discussion on **a general, in-protocol incentivization mechanism for relayers**. -Initially, a [simple proposal](https://github.com/cosmos/ibc/pull/577/files) was created to incentivize relaying on ICS20 token transfers on the destination chain. However, the proposal was specific to ICS20 token transfers and would have to be reimplemented in this format on every other IBC application module. +Initially, a [simple proposal](https://github.com/cosmos/ibc/pull/577/files) was created to incentivize relaying on ICS20 token transfers on the destination chain. However, the proposal was specific to ICS20 token transfers and would have to be reimplemented in this format on every other IBC application module. After much discussion, the proposal was expanded to a [general incentivisation design](https://github.com/cosmos/ibc/tree/master/spec/app/ics-029-fee-payment) that can be adopted by any ICS application protocol as [middleware](../../ibc/middleware/develop.md). -## Concepts +## Concepts -ICS29 fee payments in this middleware design are built on the assumption that sender chains are the source of incentives — the chain on which packets are sent is the same chain that fee distribution to relayer operators takes place. Therefore, the middleware enables the registering of addresses associated with each party involved in relaying the packet on the source chain, and the escrowing of fees by any party which will be paid out to each party on completion of the packet lifecycle. This registration process can be automated on start up of relayer infrastructure. +ICS29 fee payments in this middleware design are built on the assumption that sender chains are the source of incentives — the chain on which packets are incentivized is the chain that distributes fees to relayer operators. However, as part of the IBC packet flow, messages have to be submitted on both sender and destination chains. This introduces the requirement of a mapping of relayer operator's addresses on both chains. + +> To achieve the stated requirements, the **fee middleware module has two main groups of functionality**: + +- Registering of relayer addresses associated with each party involved in relaying the packet on the source chain. This registration process can be automated on start up of relayer infrastructure and happens only once, not every packet flow. + + This is described in the [Fee distribution section](../ics29-fee/fee-distribution.md). + +- Escrowing fees by any party which will be paid out to each rightful party on completion of the packet lifecycle. + + This is described in the [Fee messages section](../ics29-fee/msgs.md). + +We complete the introduction by giving a list of definitions of relevant terminolgy. `Forward relayer`: The relayer that submits the `MsgRecvPacket` message for a given packet (on the destination chain). diff --git a/docs/migrations/support-denoms-with-slashes.md b/docs/migrations/support-denoms-with-slashes.md index fdbcfa64209..da280aaf690 100644 --- a/docs/migrations/support-denoms-with-slashes.md +++ b/docs/migrations/support-denoms-with-slashes.md @@ -9,7 +9,7 @@ There are four sections based on the four potential user groups of this document - Relayers - IBC Light Clients -This document is necessary when chains are upgrading from a version that does not support base denoms with slashes (e.g. v3.0.0) to a version that does (e.g. v3.1.0). All versions of ibc-go smaller than v1.5.0 for the v1.x release line, v2.3.0 for the v2.x release line, and v3.1.0 for the v3.x release line do *NOT** support IBC token transfers of coins whose base denoms contain slashes. Therefore the in-place of genesis migration described in this document are required when upgrading. +This document is necessary when chains are upgrading from a version that does not support base denoms with slashes (e.g. v3.0.0) to a version that does (e.g. v3.2.0). All versions of ibc-go smaller than v1.5.0 for the v1.x release line, v2.3.0 for the v2.x release line, and v3.1.0 for the v3.x release line do **NOT** support IBC token transfers of coins whose base denoms contain slashes. Therefore the in-place of genesis migration described in this document are required when upgrading. If a chain receives coins of a base denom with slashes before it upgrades to supporting it, the receive may pass however the trace information will be incorrect. @@ -28,41 +28,17 @@ The transfer module will now support slashes in base denoms, so we must iterate ### Upgrade Proposal ```go -// Here the upgrade name is the upgrade name set by the chain -app.UpgradeKeeper.SetUpgradeHandler("supportSlashedDenomsUpgrade", +app.UpgradeKeeper.SetUpgradeHandler("MigrateTraces", func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - // list of traces that must replace the old traces in store - var newTraces []ibctransfertypes.DenomTrace - app.TransferKeeper.IterateDenomTraces(ctx, - func(dt ibctransfertypes.DenomTrace) bool { - // check if the new way of splitting FullDenom - // into Trace and BaseDenom passes validation and - // is the same as the current DenomTrace. - // If it isn't then store the new DenomTrace in the list of new traces. - newTrace := ibctransfertypes.ParseDenomTrace(dt.GetFullDenomPath()) - if err := newTrace.Validate(); err == nil && !equalTraces(newTrace, dt) { - newTraces = append(newTraces, newTrace) - } - - return false - }) - - // replace the outdated traces with the new trace information - for _, nt := range newTraces { - app.TransferKeeper.SetDenomTrace(ctx, nt) - } - + // transfer module consensus version has been bumped to 2 return app.mm.RunMigrations(ctx, app.configurator, fromVM) }) - -func equalTraces(dtA, dtB ibctransfertypes.DenomTrace) bool { - return dtA.BaseDenom == dtB.BaseDenom && dtA.Path == dtB.Path -} + ``` This is only necessary if there are denom traces in the store with incorrect trace information from previously received coins that had a slash in the base denom. However, it is recommended that any chain upgrading to support base denominations with slashes runs this code for safety. -For a more detailed sample, please check out the code changes in [this pull request](https://github.com/cosmos/ibc-go/pull/1527). +For a more detailed sample, please check out the code changes in [this pull request](https://github.com/cosmos/ibc-go/pull/1680). ### Genesis Migration diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index a37f74cf420..537764a2883 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -63,7 +63,7 @@ For example, if a chain chooses not to integrate a controller submodule, it may #### Add `StoreUpgrades` for ICS27 module -For ICS27 it is also necessary to [manually add store upgrades](https://docs.cosmos.network/v0.44/core/upgrade.html#add-storeupgrades-for-new-modules) for the new ICS27 module and then configure the store loader to apply those upgrades in `app.go`: +For ICS27 it is also necessary to [manually add store upgrades](https://docs.cosmos.network/v0.45/core/upgrade.html#add-storeupgrades-for-new-modules) for the new ICS27 module and then configure the store loader to apply those upgrades in `app.go`: ```go if upgradeInfo.Name == "v3" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index 903c027ce4b..e1f99de04f3 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -18,6 +18,56 @@ No genesis or in-place migrations required when upgrading from v1 or v2 of ibc-g ## Chains +### ICS27 - Interchain Accounts + +The controller submodule implements now the 05-port `Middleware` interface instead of the 05-port `IBCModule` interface. Chains that integrate the controller submodule, need to create it with the `NewIBCMiddleware` constructor function. For example: + +```diff +- icacontroller.NewIBCModule(app.ICAControllerKeeper, icaAuthIBCModule) ++ icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) +``` + +where `icaAuthIBCModule` is the Interchain Accounts authentication IBC Module. + +### ICS29 - Fee Middleware + +The Fee Middleware module, as the name suggests, plays the role of an IBC middleware and as such must be configured by chain developers to route and handle IBC messages correctly. + +Please read the Fee Middleware [integration documentation](https://ibc.cosmos.network/main/middleware/ics29-fee/integration.html) for an in depth guide on how to congfigure the module correctly in order to incentivize IBC packets. + +Take a look at the following diff for an [example setup](https://github.com/cosmos/ibc-go/pull/1432/files#diff-d18972debee5e64f16e40807b2ae112ddbe609504a93ea5e1c80a5d489c3a08aL366) of how to incentivize ics27 channels. + +### Migration to fix support for base denoms with slashes + +As part of [v1.5.0](https://github.com/cosmos/ibc-go/releases/tag/v1.5.0), [v2.3.0](https://github.com/cosmos/ibc-go/releases/tag/v2.3.0) and [v3.1.0](https://github.com/cosmos/ibc-go/releases/tag/v3.1.0) some [migration handler code sample was documented](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/support-denoms-with-slashes.md#upgrade-proposal) that needs to run in order to correct the trace information of coins transferred using ICS20 whose base denom contains slashes. + +Based on feedback from the community we add now an improved solution to run the same migration that does not require copying a large piece of code over from the migration document, but instead requires only adding a one-line upgrade handler. + +If the chain will migrate to supporting base denoms with slashes, it must set the appropriate params during the execution of the upgrade handler in `app.go`: +```go +app.UpgradeKeeper.SetUpgradeHandler("MigrateTraces", + func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // transfer module consensus version has been bumped to 2 + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) + +``` + +If a chain receives coins of a base denom with slashes before it upgrades to supporting it, the receive may pass however the trace information will be incorrect. + +E.g. If a base denom of `testcoin/testcoin/testcoin` is sent to a chain that does not support slashes in the base denom, the receive will be successful. However, the trace information stored on the receiving chain will be: `Trace: "transfer/{channel-id}/testcoin/testcoin", BaseDenom: "testcoin"`. + +This incorrect trace information must be corrected when the chain does upgrade to fully supporting denominations with slashes. + +## IBC Apps + +### ICS03 - Connection + +Crossing hellos have been removed from 03-connection handshake negotiation. +`PreviousConnectionId` in `MsgConnectionOpenTry` has been deprecated and is no longer used by core IBC. + +`NewMsgConnectionOpenTry` no longer takes in the `PreviousConnectionId` as crossing hellos are no longer supported. A non-empty `PreviousConnectionId` will fail basic validation for this message. + ### ICS04 - Channel The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. @@ -30,6 +80,12 @@ The `NewErrorAcknowledgement` method signature has changed. It now accepts an `error` rather than a `string`. This was done in order to prevent accidental state changes. All error acknowledgements now contain a deterministic ABCI code and error message. It is the responsibility of the application developer to emit error details in events. +Crossing hellos have been removed from 04-channel handshake negotiation. +IBC Applications no longer need to account from already claimed capabilities in the `OnChanOpenTry` callback. The capability provided by core IBC must be able to be claimed with error. +`PreviousChannelId` in `MsgChannelOpenTry` has been deprecated and is no longer used by core IBC. + +`NewMsgChannelOpenTry` no longer takes in the `PreviousChannelId` as crossing hellos are no longer supported. A non-empty `PreviousChannelId` will fail basic validation for this message. + ### ICS27 - Interchain Accounts The `RegisterInterchainAccount` API has been modified to include an additional `version` argument. This change has been made in order to support ICS29 fee middleware, for relayer incentivization of ICS27 packets. @@ -91,3 +147,5 @@ if err := k.icaControllerKeeper.RegisterInterchainAccount(ctx, msg.ConnectionId, ## Relayers When using the `DenomTrace` gRPC, the full IBC denomination with the `ibc/` prefix may now be passed in. + +Crossing hellos are no longer supported by core IBC for 03-connection and 04-channel. The handshake should be completed in the logical 4 step process (INIT, TRY, ACK, CONFIRM). diff --git a/go.mod b/go.mod index 220efb0aeff..ac2fe927ff3 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,14 @@ go 1.20 module github.com/cosmos/ibc-go/v4 -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +retract [v4.0.0, v4.1.0] // depends on SDK version without dragonberry fix + +retract ( + [v4.0.0, v4.1.0] // depends on SDK version without dragonberry fix + v4.1.1 // contains huckleberry vulnerability + v4.2.0 // contains huckleberry vulnerability + v4.3.0 // contains huckleberry vulnerability +) require ( github.com/Finschia/finschia-sdk v0.47.1-0.20230725074611-f8840edecbaa @@ -130,3 +137,5 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.6 // indirect ) + +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/modules/apps/27-interchain-accounts/controller/client/cli/cli.go b/modules/apps/27-interchain-accounts/controller/client/cli/cli.go index 0d2f54bd59b..7aa86cc0e46 100644 --- a/modules/apps/27-interchain-accounts/controller/client/cli/cli.go +++ b/modules/apps/27-interchain-accounts/controller/client/cli/cli.go @@ -14,6 +14,7 @@ func GetQueryCmd() *cobra.Command { } queryCmd.AddCommand( + GetCmdQueryInterchainAccount(), GetCmdParams(), ) diff --git a/modules/apps/27-interchain-accounts/controller/client/cli/query.go b/modules/apps/27-interchain-accounts/controller/client/cli/query.go index 2c14a25695e..9180de57052 100644 --- a/modules/apps/27-interchain-accounts/controller/client/cli/query.go +++ b/modules/apps/27-interchain-accounts/controller/client/cli/query.go @@ -11,6 +11,40 @@ import ( "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" ) +// GetCmdQueryInterchainAccount returns the command handler for the controller submodule parameter querying. +func GetCmdQueryInterchainAccount() *cobra.Command { + cmd := &cobra.Command{ + Use: "interchain-account [owner] [connection-id]", + Short: "Query the interchain account address for a given owner on a particular connection", + Long: "Query the controller submodule for the interchain account address for a given owner on a particular connection", + Args: cobra.ExactArgs(2), + Example: fmt.Sprintf("%s query interchain-accounts controller interchain-account cosmos1layxcsmyye0dc0har9sdfzwckaz8sjwlfsj8zs connection-0", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryInterchainAccountRequest{ + Owner: args[0], + ConnectionId: args[1], + } + + res, err := queryClient.InterchainAccount(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + // GetCmdParams returns the command handler for the controller submodule parameter querying. func GetCmdParams() *cobra.Command { cmd := &cobra.Command{ diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go index 44c1ced71b9..ce25687b2e3 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go @@ -6,7 +6,6 @@ import ( sdk "github.com/Finschia/finschia-sdk/types" capabilitytypes "github.com/Finschia/finschia-sdk/x/capability/types" - "github.com/Finschia/ostracon/crypto" "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" @@ -19,12 +18,6 @@ import ( ) var ( - // TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module() - // https://github.com/cosmos/cosmos-sdk/issues/10225 - // - // TestAccAddress defines a resuable bech32 address for testing purposes - TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID) - // TestOwnerAddress defines a reusable bech32 address for testing purposes TestOwnerAddress = "link17dtl0mjt3t77kpuhg2edqzjpszulwhgzfu9afc" @@ -244,7 +237,7 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenTry() { proofInit, proofHeight := path.EndpointB.Chain.QueryProof(channelKey) // use chainA (controller) for ChanOpenTry - msg := channeltypes.NewMsgChannelOpenTry(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, TestVersion, channeltypes.ORDERED, []string{path.EndpointA.ConnectionID}, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, TestVersion, proofInit, proofHeight, icatypes.ModuleName) + msg := channeltypes.NewMsgChannelOpenTry(path.EndpointA.ChannelConfig.PortID, TestVersion, channeltypes.ORDERED, []string{path.EndpointA.ConnectionID}, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, TestVersion, proofInit, proofHeight, icatypes.ModuleName) handler := suite.chainA.GetSimApp().MsgServiceRouter().Handler(msg) _, err = handler(suite.chainA.GetContext(), msg) @@ -490,7 +483,7 @@ func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() { 0, ) - ack := cbs.OnRecvPacket(suite.chainA.GetContext(), packet, TestAccAddress) + ack := cbs.OnRecvPacket(suite.chainA.GetContext(), packet, nil) suite.Require().Equal(tc.expPass, ack.Success()) }) } diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account.go b/modules/apps/27-interchain-accounts/controller/keeper/account.go index fa11da85afa..53905910b51 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account.go @@ -3,6 +3,7 @@ package keeper import ( sdk "github.com/Finschia/finschia-sdk/types" sdkerrors "github.com/Finschia/finschia-sdk/types/errors" + authtypes "github.com/Finschia/finschia-sdk/x/auth/types" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" @@ -38,7 +39,7 @@ func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, owner, } } - msg := channeltypes.NewMsgChannelOpenInit(portID, version, channeltypes.ORDERED, []string{connectionID}, icatypes.PortID, icatypes.ModuleName) + msg := channeltypes.NewMsgChannelOpenInit(portID, version, channeltypes.ORDERED, []string{connectionID}, icatypes.PortID, authtypes.NewModuleAddress(icatypes.ModuleName).String()) handler := k.msgRouter.Handler(msg) res, err := handler(ctx, msg) diff --git a/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go b/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go index 348294fbc8a..bce859fc464 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go @@ -10,6 +10,7 @@ import ( func (suite *KeeperTestSuite) TestInitGenesis() { suite.SetupTest() + interchainAccAddr := icatypes.GenerateUniqueAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, TestPortID) genesisState := icatypes.ControllerGenesisState{ ActiveChannels: []icatypes.ActiveChannel{ { @@ -22,7 +23,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() { { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr.String(), }, }, Ports: []string{TestPortID}, @@ -36,7 +37,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() { accountAdrr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, TestPortID) suite.Require().True(found) - suite.Require().Equal(TestAccAddress.String(), accountAdrr) + suite.Require().Equal(interchainAccAddr.String(), accountAdrr) expParams := types.NewParams(false) params := suite.chainA.GetSimApp().ICAControllerKeeper.GetParams(suite.chainA.GetContext()) @@ -52,12 +53,15 @@ func (suite *KeeperTestSuite) TestExportGenesis() { err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) + interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + genesisState := keeper.ExportGenesis(suite.chainA.GetContext(), suite.chainA.GetSimApp().ICAControllerKeeper) suite.Require().Equal(path.EndpointA.ChannelID, genesisState.ActiveChannels[0].ChannelId) suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.ActiveChannels[0].PortId) - suite.Require().Equal(TestAccAddress.String(), genesisState.InterchainAccounts[0].AccountAddress) + suite.Require().Equal(interchainAccAddr, genesisState.InterchainAccounts[0].AccountAddress) suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.InterchainAccounts[0].PortId) suite.Require().Equal([]string{TestPortID}, genesisState.GetPorts()) diff --git a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go index 5659e2a01be..8fee7a3800f 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go @@ -4,16 +4,41 @@ import ( "context" sdk "github.com/Finschia/finschia-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" ) var _ types.QueryServer = Keeper{} +// InterchainAccount implements the Query/InterchainAccount gRPC method +func (k Keeper) InterchainAccount(goCtx context.Context, req *types.QueryInterchainAccountRequest) (*types.QueryInterchainAccountResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + portID, err := icatypes.NewControllerPortID(req.Owner) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to generate portID from owner address: %s", err) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + addr, found := k.GetInterchainAccountAddress(ctx, req.ConnectionId, portID) + if !found { + return nil, status.Errorf(codes.NotFound, "failed to retrieve account address for %s on connection %s", portID, req.ConnectionId) + } + + return &types.QueryInterchainAccountResponse{ + Address: addr, + }, nil +} + // Params implements the Query/Params gRPC method -func (q Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { +func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { ctx := sdk.UnwrapSDKContext(c) - params := q.GetParams(ctx) + params := k.GetParams(ctx) return &types.QueryParamsResponse{ Params: ¶ms, diff --git a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go index 06bd422facb..1b7bb3dfdad 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go @@ -4,8 +4,77 @@ import ( sdk "github.com/Finschia/finschia-sdk/types" "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" + ibctesting "github.com/cosmos/ibc-go/v4/testing" ) +func (suite *KeeperTestSuite) TestQueryInterchainAccount() { + var req *types.QueryInterchainAccountRequest + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "empty owner address", + func() { + req.Owner = "" + }, + false, + }, + { + "invalid connection, account address not found", + func() { + req.ConnectionId = "invalid-connection-id" + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, ibctesting.TestAccAddress) + suite.Require().NoError(err) + + req = &types.QueryInterchainAccountRequest{ + ConnectionId: ibctesting.FirstConnectionID, + Owner: ibctesting.TestAccAddress, + } + + tc.malleate() + + res, err := suite.chainA.GetSimApp().ICAControllerKeeper.InterchainAccount(sdk.WrapSDKContext(suite.chainA.GetContext()), req) + + if tc.expPass { + expAddress, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + + suite.Require().NoError(err) + suite.Require().Equal(expAddress, res.Address) + } else { + suite.Require().Error(err) + } + }) + } +} + func (suite *KeeperTestSuite) TestQueryParams() { ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) expParams := types.DefaultParams() diff --git a/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go index ecb744b232f..5d4266e9db7 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go @@ -24,9 +24,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() { }{ { "success", - func() { - path.EndpointA.SetChannel(*channel) - }, + func() {}, true, }, { @@ -54,30 +52,44 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() { }, true, }, + { + "success: channel reopening", + func() { + err := SetupICAPath(path, TestOwnerAddress) + suite.Require().NoError(err) + + err = path.EndpointA.SetChannelClosed() + suite.Require().NoError(err) + + err = path.EndpointB.SetChannelClosed() + suite.Require().NoError(err) + + path.EndpointA.ChannelID = "" + path.EndpointB.ChannelID = "" + }, + true, + }, { "invalid metadata - previous metadata is different", func() { // set active channel to closed suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + // attempt to downgrade version by reinitializing channel with version 1, but setting channel to version 2 + metadata.Version = "ics27-2" + + versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) + suite.Require().NoError(err) + counterparty := channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) closedChannel := channeltypes.Channel{ State: channeltypes.CLOSED, Ordering: channeltypes.ORDERED, Counterparty: counterparty, ConnectionHops: []string{path.EndpointA.ConnectionID}, - Version: TestVersion, + Version: string(versionBytes), } - path.EndpointA.SetChannel(closedChannel) - - // modify metadata - metadata.Version = "ics27-2" - - versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) - suite.Require().NoError(err) - - channel.Version = string(versionBytes) }, false, }, @@ -384,7 +396,10 @@ func (suite *KeeperTestSuite) TestOnChanOpenAck() { err = path.EndpointB.ChanOpenTry() suite.Require().NoError(err) - metadata = icatypes.NewMetadata(icatypes.Version, ibctesting.FirstConnectionID, ibctesting.FirstConnectionID, TestAccAddress.String(), icatypes.EncodingProtobuf, icatypes.TxTypeSDKMultiMsg) + interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + + metadata = icatypes.NewMetadata(icatypes.Version, ibctesting.FirstConnectionID, ibctesting.FirstConnectionID, interchainAccAddr, icatypes.EncodingProtobuf, icatypes.TxTypeSDKMultiMsg) versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) suite.Require().NoError(err) diff --git a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go index 37a108e2a96..8a158ec893e 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go @@ -3,8 +3,6 @@ package keeper_test import ( "testing" - sdk "github.com/Finschia/finschia-sdk/types" - "github.com/Finschia/ostracon/crypto" "github.com/stretchr/testify/suite" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" @@ -13,12 +11,6 @@ import ( ) var ( - // TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module() - // https://github.com/cosmos/cosmos-sdk/issues/10225 - // - // TestAccAddress defines a resuable bech32 address for testing purposes - TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID) - // TestOwnerAddress defines a reusable bech32 address for testing purposes TestOwnerAddress = "link17dtl0mjt3t77kpuhg2edqzjpszulwhgzfu9afc" @@ -152,11 +144,10 @@ func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() { suite.Require().NoError(err) counterpartyPortID := path.EndpointA.ChannelConfig.PortID - expectedAddr := icatypes.GenerateAddress(suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(icatypes.ModuleName), ibctesting.FirstConnectionID, counterpartyPortID) retrievedAddr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, counterpartyPortID) suite.Require().True(found) - suite.Require().Equal(expectedAddr.String(), retrievedAddr) + suite.Require().NotEmpty(retrievedAddr) retrievedAddr, found = suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), "invalid conn", "invalid port") suite.Require().False(found) @@ -211,13 +202,16 @@ func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() { err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) + interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + suite.chainA.GetSimApp().ICAControllerKeeper.SetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedAccAddr) expectedAccounts := []icatypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr, }, { ConnectionId: ibctesting.FirstConnectionID, diff --git a/modules/apps/27-interchain-accounts/controller/types/query.pb.go b/modules/apps/27-interchain-accounts/controller/types/query.pb.go index 82a755409f4..1f42b372836 100644 --- a/modules/apps/27-interchain-accounts/controller/types/query.pb.go +++ b/modules/apps/27-interchain-accounts/controller/types/query.pb.go @@ -6,6 +6,7 @@ package types import ( context "context" fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" @@ -28,6 +29,104 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// QueryInterchainAccountRequest is the request type for the Query/InterchainAccount RPC method. +type QueryInterchainAccountRequest struct { + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` +} + +func (m *QueryInterchainAccountRequest) Reset() { *m = QueryInterchainAccountRequest{} } +func (m *QueryInterchainAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryInterchainAccountRequest) ProtoMessage() {} +func (*QueryInterchainAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_df0d8b259d72854e, []int{0} +} +func (m *QueryInterchainAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryInterchainAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryInterchainAccountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryInterchainAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryInterchainAccountRequest.Merge(m, src) +} +func (m *QueryInterchainAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryInterchainAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryInterchainAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryInterchainAccountRequest proto.InternalMessageInfo + +func (m *QueryInterchainAccountRequest) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *QueryInterchainAccountRequest) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +// QueryInterchainAccountResponse the response type for the Query/InterchainAccount RPC method. +type QueryInterchainAccountResponse struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryInterchainAccountResponse) Reset() { *m = QueryInterchainAccountResponse{} } +func (m *QueryInterchainAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryInterchainAccountResponse) ProtoMessage() {} +func (*QueryInterchainAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_df0d8b259d72854e, []int{1} +} +func (m *QueryInterchainAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryInterchainAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryInterchainAccountResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryInterchainAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryInterchainAccountResponse.Merge(m, src) +} +func (m *QueryInterchainAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryInterchainAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryInterchainAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryInterchainAccountResponse proto.InternalMessageInfo + +func (m *QueryInterchainAccountResponse) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + // QueryParamsRequest is the request type for the Query/Params RPC method. type QueryParamsRequest struct { } @@ -36,7 +135,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_df0d8b259d72854e, []int{0} + return fileDescriptor_df0d8b259d72854e, []int{2} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -75,7 +174,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_df0d8b259d72854e, []int{1} + return fileDescriptor_df0d8b259d72854e, []int{3} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -112,6 +211,8 @@ func (m *QueryParamsResponse) GetParams() *Params { } func init() { + proto.RegisterType((*QueryInterchainAccountRequest)(nil), "ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountRequest") + proto.RegisterType((*QueryInterchainAccountResponse)(nil), "ibc.applications.interchain_accounts.controller.v1.QueryInterchainAccountResponse") proto.RegisterType((*QueryParamsRequest)(nil), "ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse") } @@ -121,27 +222,37 @@ func init() { } var fileDescriptor_df0d8b259d72854e = []byte{ - // 315 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x91, 0x31, 0x4b, 0x33, 0x31, - 0x1c, 0xc6, 0x9b, 0x17, 0xde, 0x0e, 0x71, 0x8b, 0x0e, 0x52, 0x24, 0x48, 0x27, 0x97, 0x26, 0xf4, - 0x2c, 0x08, 0x1d, 0x1c, 0x14, 0x74, 0xad, 0x1d, 0x5d, 0x24, 0x17, 0xc3, 0x35, 0x72, 0x97, 0x7f, - 0x9a, 0xe4, 0x0a, 0x5d, 0xfd, 0x04, 0x82, 0x5f, 0xca, 0xb1, 0x20, 0x82, 0x9b, 0x72, 0xe7, 0x07, - 0x91, 0xde, 0x1d, 0xb4, 0x62, 0x07, 0xad, 0x6b, 0xfe, 0x3c, 0xbf, 0x5f, 0x1e, 0x1e, 0x7c, 0xaa, - 0x63, 0xc9, 0x85, 0xb5, 0xa9, 0x96, 0x22, 0x68, 0x30, 0x9e, 0x6b, 0x13, 0x94, 0x93, 0x13, 0xa1, - 0xcd, 0x8d, 0x90, 0x12, 0x72, 0x13, 0x3c, 0x97, 0x60, 0x82, 0x83, 0x34, 0x55, 0x8e, 0xcf, 0xfa, - 0x7c, 0x9a, 0x2b, 0x37, 0x67, 0xd6, 0x41, 0x00, 0x12, 0xe9, 0x58, 0xb2, 0xf5, 0x3c, 0xdb, 0x90, - 0x67, 0xab, 0x3c, 0x9b, 0xf5, 0x3b, 0xe7, 0x5b, 0x38, 0xd7, 0x08, 0x95, 0xb8, 0x73, 0x90, 0x00, - 0x24, 0xa9, 0xe2, 0xc2, 0x6a, 0x2e, 0x8c, 0x81, 0xd0, 0xe8, 0xab, 0x6b, 0x77, 0x0f, 0x93, 0xab, - 0xe5, 0x2f, 0x47, 0xc2, 0x89, 0xcc, 0x8f, 0xd5, 0x34, 0x57, 0x3e, 0x74, 0x35, 0xde, 0xfd, 0xf2, - 0xea, 0x2d, 0x18, 0xaf, 0xc8, 0x18, 0xb7, 0x6d, 0xf5, 0xb2, 0x8f, 0x0e, 0xd1, 0xd1, 0x4e, 0x34, - 0x64, 0xbf, 0x2f, 0xc5, 0x1a, 0x66, 0x43, 0x8a, 0xde, 0x10, 0xfe, 0x5f, 0xb9, 0xc8, 0x0b, 0xc2, - 0xed, 0xfa, 0x48, 0x2e, 0xb6, 0x01, 0x7f, 0xef, 0xd1, 0xb9, 0xfc, 0x33, 0xa7, 0x6e, 0xde, 0x1d, - 0xde, 0x3f, 0x7f, 0x3c, 0xfe, 0x1b, 0x90, 0x88, 0x37, 0x93, 0xfc, 0x64, 0x8a, 0xba, 0xe1, 0xd9, - 0xdd, 0x53, 0x41, 0xd1, 0xa2, 0xa0, 0xe8, 0xbd, 0xa0, 0xe8, 0xa1, 0xa4, 0xad, 0x45, 0x49, 0x5b, - 0xaf, 0x25, 0x6d, 0x5d, 0x8f, 0x12, 0x1d, 0x26, 0x79, 0xcc, 0x24, 0x64, 0x5c, 0x82, 0xcf, 0xc0, - 0x2f, 0xf1, 0xbd, 0x04, 0xf8, 0x6c, 0xc0, 0x33, 0xb8, 0xcd, 0x53, 0xe5, 0x6b, 0x59, 0x74, 0xd2, - 0x5b, 0xf9, 0x7a, 0x9b, 0x7c, 0x61, 0x6e, 0x95, 0x8f, 0xdb, 0xd5, 0xaa, 0xc7, 0x9f, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x9b, 0x4f, 0xc0, 0x9d, 0xae, 0x02, 0x00, 0x00, + // 465 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x41, 0x6b, 0x14, 0x31, + 0x14, 0xc7, 0x77, 0x56, 0xba, 0x62, 0xd4, 0x83, 0x71, 0x0f, 0xcb, 0xa2, 0xa3, 0xcc, 0xc9, 0xcb, + 0x26, 0x74, 0x2c, 0x08, 0x0b, 0x0a, 0x56, 0x50, 0x7a, 0x6b, 0xe7, 0x20, 0xe2, 0xc1, 0x92, 0xcd, + 0x84, 0x69, 0x64, 0x26, 0x6f, 0x9a, 0x64, 0x56, 0x96, 0xd2, 0x8b, 0x9f, 0x40, 0xf0, 0xe6, 0x27, + 0xf2, 0x58, 0x10, 0xc1, 0x93, 0xc8, 0xae, 0x9f, 0xc0, 0xb3, 0x07, 0x99, 0x4c, 0x74, 0x3b, 0x58, + 0xc5, 0x5d, 0x7b, 0x9a, 0xbc, 0xf7, 0x78, 0xff, 0xdf, 0x7b, 0xf9, 0x67, 0xd0, 0x03, 0x39, 0xe1, + 0x94, 0x95, 0x65, 0x2e, 0x39, 0xb3, 0x12, 0x94, 0xa1, 0x52, 0x59, 0xa1, 0xf9, 0x01, 0x93, 0x6a, + 0x9f, 0x71, 0x0e, 0x95, 0xb2, 0x86, 0x72, 0x50, 0x56, 0x43, 0x9e, 0x0b, 0x4d, 0xa7, 0x9b, 0xf4, + 0xb0, 0x12, 0x7a, 0x46, 0x4a, 0x0d, 0x16, 0x70, 0x2c, 0x27, 0x9c, 0x9c, 0xee, 0x27, 0x67, 0xf4, + 0x93, 0x65, 0x3f, 0x99, 0x6e, 0x0e, 0x1f, 0xad, 0xc1, 0x3c, 0xa5, 0xe0, 0xc0, 0xc3, 0x7e, 0x06, + 0x19, 0xb8, 0x23, 0xad, 0x4f, 0x3e, 0x7b, 0x23, 0x03, 0xc8, 0x72, 0x41, 0x59, 0x29, 0x29, 0x53, + 0x0a, 0xac, 0x1f, 0xca, 0x55, 0x23, 0x8b, 0x6e, 0xee, 0xd5, 0xb3, 0xef, 0xfc, 0xc2, 0x3d, 0x6c, + 0x68, 0x89, 0x38, 0xac, 0x84, 0xb1, 0xb8, 0x8f, 0x36, 0xe0, 0x95, 0x12, 0x7a, 0x10, 0xdc, 0x0e, + 0xee, 0x5c, 0x4a, 0x9a, 0x00, 0xdf, 0x47, 0x57, 0x39, 0x28, 0x25, 0x78, 0xad, 0xb5, 0x2f, 0xd3, + 0x41, 0xb7, 0xae, 0x6e, 0x0f, 0xbe, 0x7d, 0xbe, 0xd5, 0x9f, 0xb1, 0x22, 0x1f, 0x47, 0xad, 0x72, + 0x94, 0x5c, 0x59, 0xc6, 0x3b, 0x69, 0x34, 0x46, 0xe1, 0x9f, 0xa8, 0xa6, 0x04, 0x65, 0x04, 0x1e, + 0xa0, 0x8b, 0x2c, 0x4d, 0xb5, 0x30, 0xc6, 0x83, 0x7f, 0x86, 0x51, 0x1f, 0x61, 0xd7, 0xbb, 0xcb, + 0x34, 0x2b, 0x8c, 0x1f, 0x33, 0x92, 0xe8, 0x7a, 0x2b, 0xeb, 0x65, 0x12, 0xd4, 0x2b, 0x5d, 0xc6, + 0xa9, 0x5c, 0x8e, 0xc7, 0x64, 0x75, 0x73, 0x88, 0xd7, 0xf4, 0x4a, 0xf1, 0xf7, 0x0b, 0x68, 0xc3, + 0xb1, 0xf0, 0xbb, 0x2e, 0xba, 0xf6, 0xdb, 0x0a, 0x78, 0x6f, 0x1d, 0xc6, 0x5f, 0x4d, 0x18, 0x26, + 0xe7, 0x29, 0xd9, 0x5c, 0x4d, 0xf4, 0xe2, 0xf5, 0x87, 0xaf, 0x6f, 0xbb, 0xcf, 0xf0, 0x53, 0xea, + 0xdf, 0xde, 0xbf, 0xbc, 0x39, 0xe7, 0xbe, 0xa1, 0x47, 0xee, 0x7b, 0x4c, 0x97, 0xa6, 0x1a, 0x7a, + 0xd4, 0x72, 0xfc, 0x18, 0x7f, 0x0c, 0x50, 0xaf, 0xb9, 0x39, 0xfc, 0x78, 0xed, 0xf1, 0x5b, 0x26, + 0x0f, 0x9f, 0xfc, 0xb7, 0x8e, 0xdf, 0x7d, 0xec, 0x76, 0xdf, 0xc2, 0xf1, 0x2a, 0xbb, 0x37, 0xf6, + 0x6f, 0xbf, 0x7c, 0x3f, 0x0f, 0x83, 0x93, 0x79, 0x18, 0x7c, 0x99, 0x87, 0xc1, 0x9b, 0x45, 0xd8, + 0x39, 0x59, 0x84, 0x9d, 0x4f, 0x8b, 0xb0, 0xf3, 0x7c, 0x37, 0x93, 0xf6, 0xa0, 0x9a, 0x10, 0x0e, + 0x05, 0xe5, 0x60, 0x0a, 0x30, 0xb5, 0xfc, 0x28, 0x03, 0x3a, 0xdd, 0xa2, 0x05, 0xa4, 0x55, 0x2e, + 0x4c, 0x03, 0x8b, 0xef, 0x8d, 0x96, 0xbc, 0xd1, 0x59, 0x3c, 0x3b, 0x2b, 0x85, 0x99, 0xf4, 0xdc, + 0x4f, 0x7a, 0xf7, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x05, 0x64, 0x1e, 0x93, 0x04, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -156,6 +267,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { + // InterchainAccount returns the interchain account address for a given owner address on a given connection + InterchainAccount(ctx context.Context, in *QueryInterchainAccountRequest, opts ...grpc.CallOption) (*QueryInterchainAccountResponse, error) // Params queries all parameters of the ICA controller submodule. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) } @@ -168,6 +281,15 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } +func (c *queryClient) InterchainAccount(ctx context.Context, in *QueryInterchainAccountRequest, opts ...grpc.CallOption) (*QueryInterchainAccountResponse, error) { + out := new(QueryInterchainAccountResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { out := new(QueryParamsResponse) err := c.cc.Invoke(ctx, "/ibc.applications.interchain_accounts.controller.v1.Query/Params", in, out, opts...) @@ -179,6 +301,8 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . // QueryServer is the server API for Query service. type QueryServer interface { + // InterchainAccount returns the interchain account address for a given owner address on a given connection + InterchainAccount(context.Context, *QueryInterchainAccountRequest) (*QueryInterchainAccountResponse, error) // Params queries all parameters of the ICA controller submodule. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) } @@ -187,6 +311,9 @@ type QueryServer interface { type UnimplementedQueryServer struct { } +func (*UnimplementedQueryServer) InterchainAccount(ctx context.Context, req *QueryInterchainAccountRequest) (*QueryInterchainAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterchainAccount not implemented") +} func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } @@ -195,6 +322,24 @@ func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) } +func _Query_InterchainAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryInterchainAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).InterchainAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.interchain_accounts.controller.v1.Query/InterchainAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).InterchainAccount(ctx, req.(*QueryInterchainAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryParamsRequest) if err := dec(in); err != nil { @@ -217,6 +362,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "ibc.applications.interchain_accounts.controller.v1.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "InterchainAccount", + Handler: _Query_InterchainAccount_Handler, + }, { MethodName: "Params", Handler: _Query_Params_Handler, @@ -226,6 +375,73 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Metadata: "ibc/applications/interchain_accounts/controller/v1/query.proto", } +func (m *QueryInterchainAccountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryInterchainAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryInterchainAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryInterchainAccountResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryInterchainAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryInterchainAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -295,6 +511,36 @@ func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *QueryInterchainAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryInterchainAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func (m *QueryParamsRequest) Size() (n int) { if m == nil { return 0 @@ -323,6 +569,202 @@ func sovQuery(x uint64) (n int) { func sozQuery(x uint64) (n int) { return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *QueryInterchainAccountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryInterchainAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryInterchainAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryInterchainAccountResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryInterchainAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryInterchainAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/modules/apps/27-interchain-accounts/controller/types/query.pb.gw.go b/modules/apps/27-interchain-accounts/controller/types/query.pb.gw.go index a9ac1bc03bb..776716ffa61 100644 --- a/modules/apps/27-interchain-accounts/controller/types/query.pb.gw.go +++ b/modules/apps/27-interchain-accounts/controller/types/query.pb.gw.go @@ -31,6 +31,82 @@ var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +func request_Query_InterchainAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryInterchainAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := client.InterchainAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_InterchainAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryInterchainAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["owner"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "owner") + } + + protoReq.Owner, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "owner", err) + } + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := server.InterchainAccount(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryParamsRequest var metadata runtime.ServerMetadata @@ -55,6 +131,26 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + mux.Handle("GET", pattern_Query_InterchainAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_InterchainAccount_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_InterchainAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -116,6 +212,26 @@ func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc // "QueryClient" to call the correct interceptors. func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + mux.Handle("GET", pattern_Query_InterchainAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_InterchainAccount_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_InterchainAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -140,9 +256,13 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( + pattern_Query_InterchainAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8}, []string{"ibc", "apps", "interchain_accounts", "controller", "v1", "owners", "owner", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"ibc", "apps", "interchain_accounts", "controller", "v1", "params"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( + forward_Query_InterchainAccount_0 = runtime.ForwardResponseMessage + forward_Query_Params_0 = runtime.ForwardResponseMessage ) diff --git a/modules/apps/27-interchain-accounts/host/ibc_module_test.go b/modules/apps/27-interchain-accounts/host/ibc_module_test.go index 8071f5fb371..e32dc615a5c 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -7,12 +7,8 @@ import ( sdk "github.com/Finschia/finschia-sdk/types" banktypes "github.com/Finschia/finschia-sdk/x/bank/types" capabilitytypes "github.com/Finschia/finschia-sdk/x/capability/types" - "github.com/Finschia/ostracon/crypto" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/suite" - abcitypes "github.com/tendermint/tendermint/abci/types" - tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" - tmstate "github.com/tendermint/tendermint/state" "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" @@ -24,12 +20,6 @@ import ( ) var ( - // TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module() - // https://github.com/cosmos/cosmos-sdk/issues/10225 - // - // TestAccAddress defines a resuable bech32 address for testing purposes - TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID) - // TestOwnerAddress defines a reusable bech32 address for testing purposes TestOwnerAddress = "link17dtl0mjt3t77kpuhg2edqzjpszulwhgzfu9afc" @@ -150,6 +140,17 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() { { "success", func() {}, true, }, + { + "account address generation is block dependent", func() { + icaHostAccount := icatypes.GenerateUniqueAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + err := suite.chainB.GetSimApp().BankKeeper.SendCoins(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), icaHostAccount, sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(1))}) + suite.Require().NoError(err) + suite.Require().True(suite.chainB.GetSimApp().AccountKeeper.HasAccount(suite.chainB.GetContext(), icaHostAccount)) + + // ensure account registration is simulated in a separate block + suite.chainB.NextBlock() + }, true, + }, { "host submodule disabled", func() { suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), types.NewParams(false, []string{})) @@ -216,6 +217,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() { if tc.expPass { suite.Require().NoError(err) + + addr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, counterparty.PortId) + suite.Require().True(exists) + suite.Require().NotNil(addr) } else { suite.Require().Error(err) suite.Require().Equal("", version) @@ -534,7 +539,7 @@ func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() { 0, ) - err = cbs.OnAcknowledgementPacket(suite.chainB.GetContext(), packet, []byte("ackBytes"), TestAccAddress) + err = cbs.OnAcknowledgementPacket(suite.chainB.GetContext(), packet, []byte("ackBytes"), nil) if tc.expPass { suite.Require().NoError(err) @@ -587,7 +592,7 @@ func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() { 0, ) - err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, TestAccAddress) + err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, nil) if tc.expPass { suite.Require().NoError(err) @@ -613,21 +618,28 @@ func (suite *InterchainAccountsTestSuite) fundICAWallet(ctx sdk.Context, portID suite.Require().NoError(err) } -// TestControlAccountAfterChannelClose tests that a controller chain can control a registered interchain account after the currently active channel for that interchain account has been closed -// by opening a new channel on the associated portID +// TestControlAccountAfterChannelClose tests that a controller chain can control a registered interchain account after the currently active channel for that interchain account has been closed. +// A new channel will be opened for the controller portID. The interchain account address should remain unchanged. func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() { - // create channel + init interchain account on a particular port path := NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) + err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) + // two sends will be performed, one after initial creation of the account and one after channel closure and reopening + var ( + startingBal = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000))) + tokenAmt = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000))) + expBalAfterFirstSend = startingBal.Sub(tokenAmt) + expBalAfterSecondSend = expBalAfterFirstSend.Sub(tokenAmt) + ) + // check that the account is working as expected - suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10000)))) - interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, startingBal) + interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - tokenAmt := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000))) msg := &banktypes.MsgSend{ FromAddress: interchainAccountAddr, ToAddress: suite.chainB.SenderAccount.GetAddress().String(), @@ -661,8 +673,7 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() icaAddr, err := sdk.AccAddressFromBech32(interchainAccountAddr) suite.Require().NoError(err) - hasBalance := suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(5000)}) - suite.Require().True(hasBalance) + suite.assertBalance(icaAddr, expBalAfterFirstSend) // close the channel err = path.EndpointA.SetChannelClosed() @@ -688,67 +699,12 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() err = path.RelayPacket(packetRelay) suite.Require().NoError(err) // relay committed - // check that the ica balance is updated - hasBalance = suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(0)}) - suite.Require().True(hasBalance) + suite.assertBalance(icaAddr, expBalAfterSecondSend) } -// The safety of including SDK MsgResponses in the acknowledgement rests -// on the inclusion of the abcitypes.ResponseDeliverTx.Data in the -// abcitypes.ResposneDeliverTx hash. If the abcitypes.ResponseDeliverTx.Data -// gets removed from consensus they must no longer be used in the packet -// acknowledgement. -// -// This test acts as an indicator that the abcitypes.ResponseDeliverTx.Data -// may no longer be deterministic. -func (suite *InterchainAccountsTestSuite) TestABCICodeDeterminism() { - msgResponseBz, err := proto.Marshal(&channeltypes.MsgChannelOpenInitResponse{}) - suite.Require().NoError(err) - - msgData := &sdk.MsgData{ - MsgType: sdk.MsgTypeURL(&channeltypes.MsgChannelOpenInit{}), - Data: msgResponseBz, - } - - txResponse, err := proto.Marshal(&sdk.TxMsgData{ - Data: []*sdk.MsgData{msgData}, - }) - suite.Require().NoError(err) - - deliverTx := abcitypes.ResponseDeliverTx{ - Data: txResponse, - } - responses := tmprotostate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{ - &deliverTx, - }, - } - - differentMsgResponseBz, err := proto.Marshal(&channeltypes.MsgRecvPacketResponse{}) - suite.Require().NoError(err) - - differentMsgData := &sdk.MsgData{ - MsgType: sdk.MsgTypeURL(&channeltypes.MsgRecvPacket{}), - Data: differentMsgResponseBz, - } - - differentTxResponse, err := proto.Marshal(&sdk.TxMsgData{ - Data: []*sdk.MsgData{differentMsgData}, - }) - suite.Require().NoError(err) - - differentDeliverTx := abcitypes.ResponseDeliverTx{ - Data: differentTxResponse, - } - - differentResponses := tmprotostate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{ - &differentDeliverTx, - }, - } - - hash := tmstate.ABCIResponsesResultsHash(&responses) - differentHash := tmstate.ABCIResponsesResultsHash(&differentResponses) - - suite.Require().NotEqual(hash, differentHash) +// assertBalance asserts that the provided address has exactly the expected balance. +// CONTRACT: the expected balance must only contain one coin denom. +func (suite *InterchainAccountsTestSuite) assertBalance(addr sdk.AccAddress, expBalance sdk.Coins) { + balance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), addr, sdk.DefaultBondDenom) + suite.Require().Equal(expBalance[0], balance) } diff --git a/modules/apps/27-interchain-accounts/host/keeper/account.go b/modules/apps/27-interchain-accounts/host/keeper/account.go index 21baaf9d367..f55b7014d8e 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/account.go +++ b/modules/apps/27-interchain-accounts/host/keeper/account.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/Finschia/finschia-sdk/types" + sdkerrors "github.com/Finschia/finschia-sdk/types/errors" authtypes "github.com/Finschia/finschia-sdk/x/auth/types" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" @@ -10,6 +11,7 @@ import ( // RegisterInterchainAccount attempts to create a new account using the provided address and // stores it in state keyed by the provided connection and port identifiers // If an account for the provided address already exists this function returns early (no-op) +// Deprecated: This function is deprecated! Please use createInterchainAccount in favour of RegisterInterchainAccount func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, controllerPortID string, accAddress sdk.AccAddress) { if acc := k.accountKeeper.GetAccount(ctx, accAddress); acc != nil { return @@ -25,3 +27,26 @@ func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, control k.SetInterchainAccountAddress(ctx, connectionID, controllerPortID, interchainAccount.Address) } + +// createInterchainAccount creates a new interchain account. An address is generated using the host connectionID, the controller portID, +// and block dependent information. An error is returned if an account already exists for the generated account. +// An interchain account type is set in the account keeper and the interchain account address mapping is updated. +func (k Keeper) createInterchainAccount(ctx sdk.Context, connectionID, controllerPortID string) (sdk.AccAddress, error) { + accAddress := icatypes.GenerateUniqueAddress(ctx, connectionID, controllerPortID) + + if acc := k.accountKeeper.GetAccount(ctx, accAddress); acc != nil { + return nil, sdkerrors.Wrapf(icatypes.ErrAccountAlreadyExist, "existing account for newly generated interchain account address %s", accAddress) + } + + interchainAccount := icatypes.NewInterchainAccount( + authtypes.NewBaseAccountWithAddress(accAddress), + controllerPortID, + ) + + k.accountKeeper.NewAccount(ctx, interchainAccount) + k.accountKeeper.SetAccount(ctx, interchainAccount) + + k.SetInterchainAccountAddress(ctx, connectionID, controllerPortID, interchainAccount.Address) + + return accAddress, nil +} diff --git a/modules/apps/27-interchain-accounts/host/keeper/account_test.go b/modules/apps/27-interchain-accounts/host/keeper/account_test.go deleted file mode 100644 index ba665bfffba..00000000000 --- a/modules/apps/27-interchain-accounts/host/keeper/account_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package keeper_test - -import ( - sdk "github.com/Finschia/finschia-sdk/types" - - icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -func (suite *KeeperTestSuite) TestRegisterInterchainAccount() { - suite.SetupTest() - - path := NewICAPath(suite.chainA, suite.chainB) - suite.coordinator.SetupConnections(path) - - // RegisterInterchainAccount - err := SetupICAPath(path, TestOwnerAddress) - suite.Require().NoError(err) - - portID, err := icatypes.NewControllerPortID(TestOwnerAddress) - suite.Require().NoError(err) - - // Get the address of the interchain account stored in state during handshake step - storedAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, portID) - suite.Require().True(found) - - icaAddr, err := sdk.AccAddressFromBech32(storedAddr) - suite.Require().NoError(err) - - // Check if account is created - interchainAccount := suite.chainB.GetSimApp().AccountKeeper.GetAccount(suite.chainB.GetContext(), icaAddr) - suite.Require().Equal(interchainAccount.GetAddress().String(), storedAddr) -} diff --git a/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go b/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go index be6ae690385..e083296acde 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go @@ -10,6 +10,7 @@ import ( func (suite *KeeperTestSuite) TestInitGenesis() { suite.SetupTest() + interchainAccAddr := icatypes.GenerateUniqueAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, TestPortID) genesisState := icatypes.HostGenesisState{ ActiveChannels: []icatypes.ActiveChannel{ { @@ -22,7 +23,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() { { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr.String(), }, }, Port: icatypes.PortID, @@ -36,7 +37,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() { accountAdrr, found := suite.chainA.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, TestPortID) suite.Require().True(found) - suite.Require().Equal(TestAccAddress.String(), accountAdrr) + suite.Require().Equal(interchainAccAddr.String(), accountAdrr) expParams := types.NewParams(false, nil) params := suite.chainA.GetSimApp().ICAHostKeeper.GetParams(suite.chainA.GetContext()) @@ -52,12 +53,15 @@ func (suite *KeeperTestSuite) TestExportGenesis() { err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) + interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + genesisState := keeper.ExportGenesis(suite.chainB.GetContext(), suite.chainB.GetSimApp().ICAHostKeeper) suite.Require().Equal(path.EndpointB.ChannelID, genesisState.ActiveChannels[0].ChannelId) suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.ActiveChannels[0].PortId) - suite.Require().Equal(TestAccAddress.String(), genesisState.InterchainAccounts[0].AccountAddress) + suite.Require().Equal(interchainAccAddr, genesisState.InterchainAccounts[0].AccountAddress) suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.InterchainAccounts[0].PortId) suite.Require().Equal(icatypes.PortID, genesisState.GetPort()) diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake.go b/modules/apps/27-interchain-accounts/host/keeper/handshake.go index 55f0107d276..19f3f889d07 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - "strings" sdk "github.com/Finschia/finschia-sdk/types" sdkerrors "github.com/Finschia/finschia-sdk/types/errors" @@ -35,10 +34,6 @@ func (k Keeper) OnChanOpenTry( return "", sdkerrors.Wrapf(icatypes.ErrInvalidHostPort, "expected %s, got %s", icatypes.PortID, portID) } - if !strings.HasPrefix(counterparty.PortId, icatypes.PortPrefix) { - return "", sdkerrors.Wrapf(icatypes.ErrInvalidControllerPort, "expected %s{owner-account-address}, got %s", icatypes.PortPrefix, counterparty.PortId) - } - var metadata icatypes.Metadata if err := icatypes.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &metadata); err != nil { return "", sdkerrors.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain accounts metadata") @@ -70,10 +65,25 @@ func (k Keeper) OnChanOpenTry( return "", sdkerrors.Wrapf(err, "failed to claim capability for channel %s on port %s", channelID, portID) } - accAddress := icatypes.GenerateAddress(k.accountKeeper.GetModuleAddress(icatypes.ModuleName), metadata.HostConnectionId, counterparty.PortId) + var ( + accAddress sdk.AccAddress + err error + ) - // Register interchain account if it does not already exist - k.RegisterInterchainAccount(ctx, metadata.HostConnectionId, counterparty.PortId, accAddress) + interchainAccAddr, found := k.GetInterchainAccountAddress(ctx, metadata.HostConnectionId, counterparty.PortId) + if found { + // reopening an interchain account + accAddress = sdk.MustAccAddressFromBech32(interchainAccAddr) + if _, ok := k.accountKeeper.GetAccount(ctx, accAddress).(*icatypes.InterchainAccount); !ok { + return "", sdkerrors.Wrapf(icatypes.ErrInvalidAccountReopening, "existing account address %s, does not have interchain account type", accAddress) + } + + } else { + accAddress, err = k.createInterchainAccount(ctx, metadata.HostConnectionId, counterparty.PortId) + if err != nil { + return "", err + } + } metadata.Address = accAddress.String() versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) 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 205e7cececd..106a390a278 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go @@ -1,14 +1,43 @@ package keeper_test import ( + sdk "github.com/Finschia/finschia-sdk/types" + authtypes "github.com/Finschia/finschia-sdk/x/auth/types" capabilitytypes "github.com/Finschia/finschia-sdk/x/capability/types" + hosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v4/modules/core/24-host" ibctesting "github.com/cosmos/ibc-go/v4/testing" ) +// open and close channel is a helper function for TestOnChanOpenTry for reopening accounts +func (suite *KeeperTestSuite) openAndCloseChannel(path *ibctesting.Path) { + err := path.EndpointB.ChanOpenTry() + suite.Require().NoError(err) + + err = path.EndpointA.ChanOpenAck() + suite.Require().NoError(err) + + err = path.EndpointB.ChanOpenConfirm() + suite.Require().NoError(err) + + err = path.EndpointA.SetChannelClosed() + suite.Require().NoError(err) + + err = path.EndpointB.SetChannelClosed() + suite.Require().NoError(err) + + path.EndpointA.ChannelID = "" + err = RegisterInterchainAccount(path.EndpointA, TestOwnerAddress) + suite.Require().NoError(err) + + // bump channel sequence as these test mock core IBC behaviour on ChanOpenTry + channelSequence := path.EndpointB.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(path.EndpointB.Chain.GetContext()) + path.EndpointB.ChannelID = channeltypes.FormatChannelIdentifier(channelSequence) +} + func (suite *KeeperTestSuite) TestOnChanOpenTry() { var ( channel *channeltypes.Channel @@ -24,22 +53,89 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { }{ { "success", - func() { - path.EndpointB.SetChannel(*channel) - }, + func() {}, true, }, { "success - reopening closed active channel", func() { - // create a new channel and set it in state - ch := channeltypes.NewChannel(channeltypes.CLOSED, channeltypes.ORDERED, channeltypes.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID), []string{path.EndpointA.ConnectionID}, TestVersion) - suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, ch) + // create interchain account + // undo setup + path.EndpointB.ChannelID = "" + err := suite.chainB.App.GetScopedIBCKeeper().ReleaseCapability(suite.chainB.GetContext(), chanCap) + suite.Require().NoError(err) - // set the active channelID in state - suite.chainB.GetSimApp().ICAHostKeeper.SetActiveChannelID(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, path.EndpointB.ChannelID) + suite.openAndCloseChannel(path) }, true, }, + { + "success - reopening account with new address", + func() { + // create interchain account + // undo setup + path.EndpointB.ChannelID = "" + err := suite.chainB.App.GetScopedIBCKeeper().ReleaseCapability(suite.chainB.GetContext(), chanCap) + suite.Require().NoError(err) + + suite.openAndCloseChannel(path) + + // delete interchain account address + store := suite.chainB.GetContext().KVStore(suite.chainB.GetSimApp().GetKey(hosttypes.SubModuleName)) + store.Delete(icatypes.KeyOwnerAccount(path.EndpointA.ChannelConfig.PortID, path.EndpointB.ConnectionID)) + + // assert interchain account address mapping was deleted + _, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().False(found) + }, true, + }, + { + "reopening account fails - no existing account", + func() { + // create interchain account + // undo setup + path.EndpointB.ChannelID = "" + err := suite.chainB.App.GetScopedIBCKeeper().ReleaseCapability(suite.chainB.GetContext(), chanCap) + suite.Require().NoError(err) + + suite.openAndCloseChannel(path) + + // delete existing account + addr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(found) + + acc := suite.chainB.GetSimApp().AccountKeeper.GetAccount(suite.chainB.GetContext(), sdk.MustAccAddressFromBech32(addr)) + suite.chainB.GetSimApp().AccountKeeper.RemoveAccount(suite.chainB.GetContext(), acc) + }, false, + }, + { + "reopening account fails - existing account is not interchain account type", + func() { + // create interchain account + // undo setup + path.EndpointB.ChannelID = "" + err := suite.chainB.App.GetScopedIBCKeeper().ReleaseCapability(suite.chainB.GetContext(), chanCap) + suite.Require().NoError(err) + + suite.openAndCloseChannel(path) + + addr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(found) + + accAddress := sdk.MustAccAddressFromBech32(addr) + baseAcc := authtypes.NewBaseAccountWithAddress(accAddress) + suite.chainB.GetSimApp().AccountKeeper.SetAccount(suite.chainB.GetContext(), baseAcc) + }, false, + }, + { + "account already exists", + func() { + interchainAccAddr := icatypes.GenerateUniqueAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + err := suite.chainB.GetSimApp().BankKeeper.SendCoins(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), interchainAccAddr, sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(1))}) + suite.Require().NoError(err) + suite.Require().True(suite.chainB.GetSimApp().AccountKeeper.HasAccount(suite.chainB.GetContext(), interchainAccAddr)) + }, + false, + }, { "invalid metadata - previous metadata is different", func() { @@ -48,15 +144,17 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, ch) // set the active channelID in state - suite.chainB.GetSimApp().ICAHostKeeper.SetActiveChannelID(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, path.EndpointB.ChannelID) + suite.chainB.GetSimApp().ICAHostKeeper.SetActiveChannelID(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID, path.EndpointB.ChannelID) - // modify metadata + // attempt to downgrade version by reinitializing channel with version 1, but setting channel to version 2 metadata.Version = "ics27-2" versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata) suite.Require().NoError(err) - path.EndpointA.ChannelConfig.Version = string(versionBytes) + channel.Version = string(versionBytes) + + path.EndpointB.SetChannel(*channel) }, false, }, { @@ -73,13 +171,6 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { }, false, }, - { - "invalid counterparty port ID", - func() { - channel.Counterparty.PortId = "invalid-port-id" - }, - false, - }, { "connection not found", func() { @@ -218,6 +309,16 @@ func (suite *KeeperTestSuite) TestOnChanOpenTry() { if tc.expPass { suite.Require().NoError(err) + + storedAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(found) + + interchainAccAddr, err := sdk.AccAddressFromBech32(storedAddr) + suite.Require().NoError(err) + + // Check if account is created + interchainAccount := suite.chainB.GetSimApp().AccountKeeper.GetAccount(suite.chainB.GetContext(), interchainAccAddr) + suite.Require().Equal(interchainAccount.GetAddress().String(), storedAddr) } else { suite.Require().Error(err) suite.Require().Equal("", version) diff --git a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go index 75826ef6e43..77c16955ecd 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go @@ -3,8 +3,6 @@ package keeper_test import ( "testing" - sdk "github.com/Finschia/finschia-sdk/types" - "github.com/Finschia/ostracon/crypto" "github.com/stretchr/testify/suite" icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" @@ -13,12 +11,6 @@ import ( ) var ( - // TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module() - // https://github.com/cosmos/cosmos-sdk/issues/10225 - // - // TestAccAddress defines a resuable bech32 address for testing purposes - TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID) - // TestOwnerAddress defines a reusable bech32 address for testing purposes TestOwnerAddress = "link17dtl0mjt3t77kpuhg2edqzjpszulwhgzfu9afc" @@ -136,11 +128,10 @@ func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() { suite.Require().NoError(err) counterpartyPortID := path.EndpointA.ChannelConfig.PortID - expectedAddr := icatypes.GenerateAddress(suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(icatypes.ModuleName), ibctesting.FirstConnectionID, counterpartyPortID) retrievedAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, counterpartyPortID) suite.Require().True(found) - suite.Require().Equal(expectedAddr.String(), retrievedAddr) + suite.Require().NotEmpty(retrievedAddr) retrievedAddr, found = suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, "invalid port") suite.Require().False(found) @@ -195,13 +186,16 @@ func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() { err := SetupICAPath(path, TestOwnerAddress) suite.Require().NoError(err) + interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID) + suite.Require().True(exists) + suite.chainB.GetSimApp().ICAHostKeeper.SetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedAccAddr) expectedAccounts := []icatypes.RegisteredInterchainAccount{ { ConnectionId: ibctesting.FirstConnectionID, PortId: TestPortID, - AccountAddress: TestAccAddress.String(), + AccountAddress: interchainAccAddr, }, { ConnectionId: ibctesting.FirstConnectionID, diff --git a/modules/apps/27-interchain-accounts/host/types/ack_test.go b/modules/apps/27-interchain-accounts/host/types/ack_test.go deleted file mode 100644 index e1227f4cab2..00000000000 --- a/modules/apps/27-interchain-accounts/host/types/ack_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package types_test - -import ( - "testing" - - sdkerrors "github.com/Finschia/finschia-sdk/types/errors" - "github.com/stretchr/testify/suite" - abcitypes "github.com/tendermint/tendermint/abci/types" - tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" - tmstate "github.com/tendermint/tendermint/state" - - ibctesting "github.com/cosmos/ibc-go/v4/testing" -) - -const ( - gasUsed = uint64(100) - gasWanted = uint64(100) -) - -type TypesTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain -} - -func (suite *TypesTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) -} - -func TestTypesTestSuite(t *testing.T) { - suite.Run(t, new(TypesTestSuite)) -} - -// The safety of including ABCI error codes in the acknowledgement rests -// on the inclusion of these ABCI error codes in the abcitypes.ResposneDeliverTx -// hash. If the ABCI codes get removed from consensus they must no longer be used -// in the packet acknowledgement. -// -// This test acts as an indicator that the ABCI error codes may no longer be deterministic. -func (suite *TypesTestSuite) TestABCICodeDeterminism() { - // same ABCI error code used - err := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 1") - errSameABCICode := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 2") - - // different ABCI error code used - errDifferentABCICode := sdkerrors.ErrNotFound - - deliverTx := sdkerrors.ResponseDeliverTx(err, gasUsed, gasWanted, false) - responses := tmprotostate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{ - &deliverTx, - }, - } - - deliverTxSameABCICode := sdkerrors.ResponseDeliverTx(errSameABCICode, gasUsed, gasWanted, false) - responsesSameABCICode := tmprotostate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{ - &deliverTxSameABCICode, - }, - } - - deliverTxDifferentABCICode := sdkerrors.ResponseDeliverTx(errDifferentABCICode, gasUsed, gasWanted, false) - responsesDifferentABCICode := tmprotostate.ABCIResponses{ - DeliverTxs: []*abcitypes.ResponseDeliverTx{ - &deliverTxDifferentABCICode, - }, - } - - hash := tmstate.ABCIResponsesResultsHash(&responses) - hashSameABCICode := tmstate.ABCIResponsesResultsHash(&responsesSameABCICode) - hashDifferentABCICode := tmstate.ABCIResponsesResultsHash(&responsesDifferentABCICode) - - suite.Require().Equal(hash, hashSameABCICode) - suite.Require().NotEqual(hash, hashDifferentABCICode) -} diff --git a/modules/apps/27-interchain-accounts/module.go b/modules/apps/27-interchain-accounts/module.go index 8778b79880b..3d978a6f6f4 100644 --- a/modules/apps/27-interchain-accounts/module.go +++ b/modules/apps/27-interchain-accounts/module.go @@ -72,8 +72,15 @@ func (AppModuleBasic) RegisterRESTRoutes(ctx client.Context, rtr *mux.Router) { // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the interchain accounts module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - controllertypes.RegisterQueryHandlerClient(context.Background(), mux, controllertypes.NewQueryClient(clientCtx)) - hosttypes.RegisterQueryHandlerClient(context.Background(), mux, hosttypes.NewQueryClient(clientCtx)) + err := controllertypes.RegisterQueryHandlerClient(context.Background(), mux, controllertypes.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } + + err = hosttypes.RegisterQueryHandlerClient(context.Background(), mux, hosttypes.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } } // GetTxCmd implements AppModuleBasic interface @@ -144,8 +151,13 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // RegisterServices registers module services func (am AppModule) RegisterServices(cfg module.Configurator) { - controllertypes.RegisterQueryServer(cfg.QueryServer(), am.controllerKeeper) - hosttypes.RegisterQueryServer(cfg.QueryServer(), am.hostKeeper) + if am.controllerKeeper != nil { + controllertypes.RegisterQueryServer(cfg.QueryServer(), am.controllerKeeper) + } + + if am.hostKeeper != nil { + hosttypes.RegisterQueryServer(cfg.QueryServer(), am.hostKeeper) + } } // InitGenesis performs genesis initialization for the interchain accounts module. diff --git a/modules/apps/27-interchain-accounts/types/account.go b/modules/apps/27-interchain-accounts/types/account.go index 048f1d7939c..77f94eb9a5f 100644 --- a/modules/apps/27-interchain-accounts/types/account.go +++ b/modules/apps/27-interchain-accounts/types/account.go @@ -41,10 +41,24 @@ type interchainAccountPretty struct { // GenerateAddress returns an sdk.AccAddress derived using the provided module account address and connection and port identifiers. // The sdk.AccAddress returned is a sub-address of the module account, using the host chain connection ID and controller chain's port ID as the derivation key +// NOTE: this function is deprecated! func GenerateAddress(moduleAccAddr sdk.AccAddress, connectionID, portID string) sdk.AccAddress { return sdk.AccAddress(sdkaddress.Derive(moduleAccAddr, []byte(connectionID+portID))) } +// GenerateUniqueAddress returns an sdk.AccAddress derived using a host module account address, host connection ID, the controller portID, +// the current block app hash, and the current block data hash. The sdk.AccAddress returned is a sub-address of the host module account. +func GenerateUniqueAddress(ctx sdk.Context, connectionID, portID string) sdk.AccAddress { + hostModuleAcc := sdkaddress.Module(ModuleName, []byte(hostAccountsKey)) + header := ctx.BlockHeader() + + buf := []byte(connectionID + portID) + buf = append(buf, header.AppHash...) + buf = append(buf, header.DataHash...) + + return sdkaddress.Derive(hostModuleAcc, buf) +} + // ValidateAccountAddress performs basic validation of interchain account addresses, enforcing constraints // on address length and character set func ValidateAccountAddress(addr string) error { diff --git a/modules/apps/27-interchain-accounts/types/account_test.go b/modules/apps/27-interchain-accounts/types/account_test.go index 14a46135085..bf3059f5abd 100644 --- a/modules/apps/27-interchain-accounts/types/account_test.go +++ b/modules/apps/27-interchain-accounts/types/account_test.go @@ -42,11 +42,11 @@ func TestTypesTestSuite(t *testing.T) { suite.Run(t, new(TypesTestSuite)) } -func (suite *TypesTestSuite) TestGenerateAddress() { - addr := types.GenerateAddress([]byte{}, "test-connection-id", "test-port-id") +func (suite *TypesTestSuite) TestGenerateUniqueAddress() { + addr := types.GenerateUniqueAddress(suite.chainA.GetContext(), "test-connection-id", "test-port-id") accAddr, err := sdk.AccAddressFromBech32(addr.String()) - suite.Require().NoError(err, "TestGenerateAddress failed") + suite.Require().NoError(err, "TestGenerateUniqueAddress failed") suite.Require().NotEmpty(accAddr) } diff --git a/modules/apps/27-interchain-accounts/types/errors.go b/modules/apps/27-interchain-accounts/types/errors.go index e8f27fe645d..6a59ccdb883 100644 --- a/modules/apps/27-interchain-accounts/types/errors.go +++ b/modules/apps/27-interchain-accounts/types/errors.go @@ -22,4 +22,5 @@ var ( ErrInvalidHostPort = sdkerrors.Register(ModuleName, 16, "invalid host port") ErrInvalidTimeoutTimestamp = sdkerrors.Register(ModuleName, 17, "timeout timestamp must be in the future") ErrInvalidCodec = sdkerrors.Register(ModuleName, 18, "codec is not supported") + ErrInvalidAccountReopening = sdkerrors.Register(ModuleName, 19, "invalid account reopening") ) diff --git a/modules/apps/27-interchain-accounts/types/keys.go b/modules/apps/27-interchain-accounts/types/keys.go index 2bf05ffda3f..deb5602c1f3 100644 --- a/modules/apps/27-interchain-accounts/types/keys.go +++ b/modules/apps/27-interchain-accounts/types/keys.go @@ -25,6 +25,9 @@ const ( // QuerierRoute is the querier route for interchain accounts QuerierRoute = ModuleName + + // hostAccountKey is the key used when generating a module address for the host submodule + hostAccountsKey = "icahost-accounts" ) var ( diff --git a/modules/apps/27-interchain-accounts/types/packet.go b/modules/apps/27-interchain-accounts/types/packet.go index 400a210931a..df275cfb677 100644 --- a/modules/apps/27-interchain-accounts/types/packet.go +++ b/modules/apps/27-interchain-accounts/types/packet.go @@ -16,7 +16,7 @@ func (iapd InterchainAccountPacketData) ValidateBasic() error { return sdkerrors.Wrap(ErrInvalidOutgoingData, "packet data type cannot be unspecified") } - if iapd.Data == nil { + if len(iapd.Data) == 0 { return sdkerrors.Wrap(ErrInvalidOutgoingData, "packet data cannot be empty") } diff --git a/modules/apps/27-interchain-accounts/types/packet_test.go b/modules/apps/27-interchain-accounts/types/packet_test.go index bfbfa9d3802..ec1f415358b 100644 --- a/modules/apps/27-interchain-accounts/types/packet_test.go +++ b/modules/apps/27-interchain-accounts/types/packet_test.go @@ -40,6 +40,15 @@ func (suite *TypesTestSuite) TestValidateBasic() { }, { "empty data", + types.InterchainAccountPacketData{ + Type: types.EXECUTE_TX, + Data: []byte{}, + Memo: "memo", + }, + false, + }, + { + "nil data", types.InterchainAccountPacketData{ Type: types.EXECUTE_TX, Data: nil, diff --git a/modules/apps/29-fee/ibc_middleware.go b/modules/apps/29-fee/ibc_middleware.go index 226f514a355..56497ce38ab 100644 --- a/modules/apps/29-fee/ibc_middleware.go +++ b/modules/apps/29-fee/ibc_middleware.go @@ -224,7 +224,7 @@ func (im IBCMiddleware) OnRecvPacket( ack := im.app.OnRecvPacket(ctx, packet, relayer) - // incase of async aknowledgement (ack == nil) store the relayer address for use later during async WriteAcknowledgement + // in case of async aknowledgement (ack == nil) store the relayer address for use later during async WriteAcknowledgement if ack == nil { im.keeper.SetRelayerAddressForAsyncAck(ctx, channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), relayer.String()) return nil diff --git a/modules/apps/29-fee/ibc_middleware_test.go b/modules/apps/29-fee/ibc_middleware_test.go index 08430465e5d..bb0785b8b46 100644 --- a/modules/apps/29-fee/ibc_middleware_test.go +++ b/modules/apps/29-fee/ibc_middleware_test.go @@ -141,38 +141,27 @@ func (suite *FeeTestSuite) TestOnChanOpenTry() { testCases := []struct { name string cpVersion string - crossing bool expPass bool }{ { "success - valid fee middleware version", string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - false, true, }, { "success - valid mock version", ibcmock.Version, - false, - true, - }, - { - "success - crossing hellos: valid fee middleware", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - true, true, }, { "invalid fee middleware version", string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), false, - false, }, { "invalid mock version", string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), false, - false, }, } @@ -201,14 +190,9 @@ func (suite *FeeTestSuite) TestOnChanOpenTry() { ok bool err error ) - if tc.crossing { - suite.path.EndpointA.ChanOpenInit() - chanCap, ok = suite.chainA.GetSimApp().ScopedFeeMockKeeper.GetCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) - suite.Require().True(ok) - } else { - chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) - suite.Require().NoError(err) - } + + chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + suite.Require().NoError(err) suite.path.EndpointA.ChannelID = ibctesting.FirstChannelID diff --git a/modules/apps/29-fee/keeper/escrow_test.go b/modules/apps/29-fee/keeper/escrow_test.go index e493246ed68..f745e405f81 100644 --- a/modules/apps/29-fee/keeper/escrow_test.go +++ b/modules/apps/29-fee/keeper/escrow_test.go @@ -76,7 +76,7 @@ func (suite *KeeperTestSuite) TestDistributeFee() { }, func() { // check if the refund acc has been refunded the timeoutFee - expectedRefundAccBal := defaultTimeoutFee[0].Add(defaultTimeoutFee[0]) + expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee[0]).Add(defaultTimeoutFee[0]) balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) suite.Require().Equal(expectedRefundAccBal, balance) }, diff --git a/modules/apps/29-fee/keeper/keeper.go b/modules/apps/29-fee/keeper/keeper.go index 60dfe0890ea..bfa82c2a2d8 100644 --- a/modules/apps/29-fee/keeper/keeper.go +++ b/modules/apps/29-fee/keeper/keeper.go @@ -121,7 +121,7 @@ func (k Keeper) DeleteFeeEnabled(ctx sdk.Context, portID, channelID string) { // fee enabled flag for the given port and channel identifiers func (k Keeper) IsFeeEnabled(ctx sdk.Context, portID, channelID string) bool { store := ctx.KVStore(k.storeKey) - return store.Get(types.KeyFeeEnabled(portID, channelID)) != nil + return store.Has(types.KeyFeeEnabled(portID, channelID)) } // GetAllFeeEnabledChannels returns a list of all ics29 enabled channels containing portID & channelID that are stored in state diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go index 4722ad3c29d..15f6bfc8518 100644 --- a/modules/apps/29-fee/keeper/msg_server.go +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -20,6 +20,15 @@ var _ types.MsgServer = Keeper{} func (k Keeper) RegisterPayee(goCtx context.Context, msg *types.MsgRegisterPayee) (*types.MsgRegisterPayeeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + payee, err := sdk.AccAddressFromBech32(msg.Payee) + if err != nil { + return nil, err + } + + if k.bankKeeper.BlockedAddr(payee) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not authorized to be a payee", payee) + } + // only register payee address if the channel exists and is fee enabled if _, found := k.channelKeeper.GetChannel(ctx, msg.PortId, msg.ChannelId); !found { return nil, channeltypes.ErrChannelNotFound @@ -78,6 +87,19 @@ func (k Keeper) PayPacketFee(goCtx context.Context, msg *types.MsgPayPacketFee) return nil, types.ErrFeeModuleLocked } + refundAcc, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + if err := k.bankKeeper.IsSendEnabledCoins(ctx, msg.Fee.Total()...); err != nil { + return nil, err + } + + if k.bankKeeper.BlockedAddr(refundAcc) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to escrow fees", refundAcc) + } + // get the next sequence sequence, found := k.GetNextSequenceSend(ctx, msg.SourcePortId, msg.SourceChannelId) if !found { @@ -110,6 +132,19 @@ func (k Keeper) PayPacketFeeAsync(goCtx context.Context, msg *types.MsgPayPacket return nil, types.ErrFeeModuleLocked } + refundAcc, err := sdk.AccAddressFromBech32(msg.PacketFee.RefundAddress) + if err != nil { + return nil, err + } + + if err := k.bankKeeper.IsSendEnabledCoins(ctx, msg.PacketFee.Fee.Total()...); err != nil { + return nil, err + } + + if k.bankKeeper.BlockedAddr(refundAcc) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to escrow fees", refundAcc) + } + nextSeqSend, found := k.GetNextSequenceSend(ctx, msg.PacketId.PortId, msg.PacketId.ChannelId) if !found { return nil, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound, "channel does not exist, portID: %s, channelID: %s", msg.PacketId.PortId, msg.PacketId.ChannelId) diff --git a/modules/apps/29-fee/keeper/msg_server_test.go b/modules/apps/29-fee/keeper/msg_server_test.go index dd7e6cd4bd1..687dd9b2b3a 100644 --- a/modules/apps/29-fee/keeper/msg_server_test.go +++ b/modules/apps/29-fee/keeper/msg_server_test.go @@ -2,18 +2,18 @@ package keeper_test import ( sdk "github.com/Finschia/finschia-sdk/types" - disttypes "github.com/Finschia/finschia-sdk/x/distribution/types" + banktypes "github.com/Finschia/finschia-sdk/x/bank/types" "github.com/cosmos/ibc-go/v4/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v4/testing" + ibcmock "github.com/cosmos/ibc-go/v4/testing/mock" ) func (suite *KeeperTestSuite) TestRegisterPayee() { - var ( - msg *types.MsgRegisterPayee - ) + var msg *types.MsgRegisterPayee testCases := []struct { name string @@ -39,6 +39,20 @@ func (suite *KeeperTestSuite) TestRegisterPayee() { suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) }, }, + { + "given payee is not an sdk address", + false, + func() { + msg.Payee = "invalid-addr" + }, + }, + { + "payee is a blocked address", + false, + func() { + msg.Payee = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(transfertypes.ModuleName).String() + }, + }, } for _, tc := range testCases { @@ -184,10 +198,22 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { }, true, }, + { + "bank send enabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + }, + true, + }, { "refund account is module account", func() { - msg.Signer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(disttypes.ModuleName).String() + suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibcmock.ModuleName, fee.Total()) + msg.Signer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(ibcmock.ModuleName).String() expPacketFee := types.NewPacketFee(fee, msg.Signer, nil) expFeesInEscrow = []types.PacketFee{expPacketFee} }, @@ -222,6 +248,25 @@ func (suite *KeeperTestSuite) TestPayPacketFee() { }, false, }, + { + "refund account is a blocked address", + func() { + blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + msg.Signer = blockedAddr.String() + }, + false, + }, + { + "bank send disabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + }, + false, + }, { "acknowledgement fee balance not found", func() { @@ -325,6 +370,17 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { }, true, }, + { + "bank send enabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + }, + true, + }, { "fee module is locked", func() { @@ -401,6 +457,25 @@ func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { }, false, }, + { + "refund account is a blocked address", + func() { + blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + msg.PacketFee.RefundAddress = blockedAddr.String() + }, + false, + }, + { + "bank send disabled for fee denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + }, + false, + }, { "acknowledgement fee balance not found", func() { diff --git a/modules/apps/29-fee/types/errors.go b/modules/apps/29-fee/types/errors.go index 3866e6afc54..d3eddd5351f 100644 --- a/modules/apps/29-fee/types/errors.go +++ b/modules/apps/29-fee/types/errors.go @@ -10,7 +10,7 @@ var ( ErrRefundAccNotFound = sdkerrors.Register(ModuleName, 3, "no account found for given refund address") ErrBalanceNotFound = sdkerrors.Register(ModuleName, 4, "balance not found for given account address") ErrFeeNotFound = sdkerrors.Register(ModuleName, 5, "there is no fee escrowed for the given packetID") - ErrRelayersNotNil = sdkerrors.Register(ModuleName, 6, "relayers must be nil. This feature is not supported") + ErrRelayersNotEmpty = sdkerrors.Register(ModuleName, 6, "relayers must not be set. This feature is not supported") ErrCounterpartyPayeeEmpty = sdkerrors.Register(ModuleName, 7, "counterparty payee must not be empty") ErrForwardRelayerAddressNotFound = sdkerrors.Register(ModuleName, 8, "forward relayer address not found") ErrFeeNotEnabled = sdkerrors.Register(ModuleName, 9, "fee module is not enabled for this channel. If this error occurs after channel setup, fee module may not be enabled") diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go index e174d8dcc8e..1be0ac5d44f 100644 --- a/modules/apps/29-fee/types/expected_keepers.go +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -40,4 +40,5 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error BlockedAddr(sdk.AccAddress) bool + IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error } diff --git a/modules/apps/29-fee/types/fee.go b/modules/apps/29-fee/types/fee.go index 7c9f702dd03..ea2586df471 100644 --- a/modules/apps/29-fee/types/fee.go +++ b/modules/apps/29-fee/types/fee.go @@ -25,9 +25,9 @@ func (p PacketFee) Validate() error { return sdkerrors.Wrap(err, "failed to convert RefundAddress into sdk.AccAddress") } - // enforce relayer is nil - if p.Relayers != nil { - return ErrRelayersNotNil + // enforce relayers are not set + if len(p.Relayers) != 0 { + return ErrRelayersNotEmpty } if err := p.Fee.Validate(); err != nil { diff --git a/modules/apps/29-fee/types/fee_test.go b/modules/apps/29-fee/types/fee_test.go index 9da1464c337..52aca468156 100644 --- a/modules/apps/29-fee/types/fee_test.go +++ b/modules/apps/29-fee/types/fee_test.go @@ -47,6 +47,13 @@ func TestPacketFeeValidation(t *testing.T) { func() {}, true, }, + { + "success with empty slice for Relayers", + func() { + packetFee.Relayers = []string{} + }, + true, + }, { "should fail when refund address is invalid", func() { @@ -102,6 +109,13 @@ func TestPacketFeeValidation(t *testing.T) { }, false, }, + { + "should fail with non empty Relayers", + func() { + packetFee.Relayers = []string{"relayer"} + }, + false, + }, } for _, tc := range testCases { @@ -113,9 +127,9 @@ func TestPacketFeeValidation(t *testing.T) { err := packetFee.Validate() if tc.expPass { - require.NoError(t, err) + require.NoError(t, err, tc.name) } else { - require.Error(t, err) + require.Error(t, err, tc.name) } } } diff --git a/modules/apps/29-fee/types/msgs.go b/modules/apps/29-fee/types/msgs.go index 4c649dc7c3d..5474c5ba2c9 100644 --- a/modules/apps/29-fee/types/msgs.go +++ b/modules/apps/29-fee/types/msgs.go @@ -133,9 +133,9 @@ func (msg MsgPayPacketFee) ValidateBasic() error { return sdkerrors.Wrap(err, "failed to convert msg.Signer into sdk.AccAddress") } - // enforce relayer is nil - if msg.Relayers != nil { - return ErrRelayersNotNil + // enforce relayer is not set + if len(msg.Relayers) != 0 { + return ErrRelayersNotEmpty } if err := msg.Fee.Validate(); err != nil { diff --git a/modules/apps/29-fee/types/msgs_test.go b/modules/apps/29-fee/types/msgs_test.go index 630b6d6fbff..61639036592 100644 --- a/modules/apps/29-fee/types/msgs_test.go +++ b/modules/apps/29-fee/types/msgs_test.go @@ -171,6 +171,13 @@ func TestMsgPayPacketFeeValidation(t *testing.T) { func() {}, true, }, + { + "success with empty relayers", + func() { + msg.Relayers = []string{} + }, + true, + }, { "invalid channelID", func() { @@ -210,9 +217,9 @@ func TestMsgPayPacketFeeValidation(t *testing.T) { err := msg.ValidateBasic() if tc.expPass { - require.NoError(t, err) + require.NoError(t, err, tc.name) } else { - require.Error(t, err) + require.Error(t, err, tc.name) } } } @@ -257,6 +264,13 @@ func TestMsgPayPacketFeeAsyncValidation(t *testing.T) { func() {}, true, }, + { + "success with empty relayers", + func() { + msg.PacketFee.Relayers = []string{} + }, + true, + }, { "invalid channelID", func() { @@ -354,9 +368,9 @@ func TestMsgPayPacketFeeAsyncValidation(t *testing.T) { err := msg.ValidateBasic() if tc.expPass { - require.NoError(t, err) + require.NoError(t, err, tc.name) } else { - require.Error(t, err) + require.Error(t, err, tc.name) } } } diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index 7bb9cb14f8d..b7e97bd42c9 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -22,6 +22,7 @@ const ( flagPacketTimeoutHeight = "packet-timeout-height" flagPacketTimeoutTimestamp = "packet-timeout-timestamp" flagAbsoluteTimeouts = "absolute-timeouts" + flagMemo = "memo" ) // NewTransferTxCmd returns the command to create a NewMsgTransfer transaction @@ -76,6 +77,11 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), return err } + memo, err := cmd.Flags().GetString(flagMemo) + if err != nil { + return err + } + // if the timeouts are not absolute, retrieve latest block height and block timestamp // for the consensus state connected to the destination port/channel if !absoluteTimeouts { @@ -113,6 +119,8 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), msg := types.NewMsgTransfer( srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, ) + msg.Memo = memo + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } @@ -120,6 +128,7 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), cmd.Flags().String(flagPacketTimeoutHeight, types.DefaultRelativePacketTimeoutHeight, "Packet timeout block height. The timeout is disabled when set to 0-0.") cmd.Flags().Uint64(flagPacketTimeoutTimestamp, types.DefaultRelativePacketTimeoutTimestamp, "Packet timeout timestamp in nanoseconds from now. Default is 10 minutes. The timeout is disabled when set to 0.") cmd.Flags().Bool(flagAbsoluteTimeouts, false, "Timeout flags are used as absolute timeouts.") + cmd.Flags().String(flagMemo, "", "Memo to be sent along with the packet.") flags.AddTxFlagsToCmd(cmd) return cmd diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index c284304200b..a66676cbbea 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -111,15 +111,9 @@ func (im IBCModule) OnChanOpenTry( return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version) } - // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos - // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) - // If module can already authenticate the capability then module already owns it so we don't need to claim - // Otherwise, module does not have channel capability and we must claim it from IBC - if !im.keeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - // Only claim channel capability passed back by IBC module if we do not already own it - if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return "", err - } + // OpenTry must claim the channelCapability that IBC passes into the callback + if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return "", err } return types.Version, nil @@ -195,10 +189,12 @@ func (im IBCModule) OnRecvPacket( } eventAttributes := []sdk.Attribute{ + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(sdk.AttributeKeySender, data.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success())), } @@ -245,6 +241,7 @@ func (im IBCModule) OnAcknowledgementPacket( sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), sdk.NewAttribute(types.AttributeKeyAck, ack.String()), ), ) @@ -291,6 +288,7 @@ func (im IBCModule) OnTimeoutPacket( sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender), sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyRefundAmount, data.Amount), + sdk.NewAttribute(types.AttributeKeyMemo, data.Memo), ), ) diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 7ca03e7601b..77761f051e7 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -124,10 +124,10 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { }, false, }, { - "capability already claimed in INIT should pass", func() { + "capability already claimed", func() { err := suite.chainA.GetSimApp().ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)) suite.Require().NoError(err) - }, true, + }, false, }, { "invalid order - ORDERED", func() { diff --git a/modules/apps/transfer/keeper/MBT_README.md b/modules/apps/transfer/keeper/MBT_README.md index 3cad1e58993..c2a62599f36 100644 --- a/modules/apps/transfer/keeper/MBT_README.md +++ b/modules/apps/transfer/keeper/MBT_README.md @@ -36,7 +36,7 @@ and executed automatically. The easiest way to run Apalache is by -[using a Docker image](https://github.com/informalsystems/apalache/blob/master/docs/manual.md#useDocker); +[using a Docker image](https://apalache.informal.systems/docs/apalache/installation/docker.html); to run Jsonatr you need to locally clone the repository, and then, after building it, add the `target/debug` directory into your `PATH`. diff --git a/modules/apps/transfer/keeper/migrations.go b/modules/apps/transfer/keeper/migrations.go new file mode 100644 index 00000000000..5e4fcffd236 --- /dev/null +++ b/modules/apps/transfer/keeper/migrations.go @@ -0,0 +1,59 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/Finschia/finschia-sdk/types" + + "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper Keeper) Migrator { + return Migrator{keeper: keeper} +} + +// MigrateTraces migrates the DenomTraces to the correct format, accounting for slashes in the BaseDenom. +func (m Migrator) MigrateTraces(ctx sdk.Context) error { + + // list of traces that must replace the old traces in store + var newTraces []types.DenomTrace + m.keeper.IterateDenomTraces(ctx, + func(dt types.DenomTrace) (stop bool) { + // check if the new way of splitting FullDenom + // is the same as the current DenomTrace. + // If it isn't then store the new DenomTrace in the list of new traces. + newTrace := types.ParseDenomTrace(dt.GetFullDenomPath()) + err := newTrace.Validate() + if err != nil { + panic(err) + } + + if dt.IBCDenom() != newTrace.IBCDenom() { + // The new form of parsing will result in a token denomination change. + // A bank migration is required. A panic should occur to prevent the + // chain from using corrupted state. + panic(fmt.Sprintf("migration will result in corrupted state. Previous IBC token (%s) requires a bank migration. Expected denom trace (%s)", dt, newTrace)) + } + + if !equalTraces(newTrace, dt) { + newTraces = append(newTraces, newTrace) + } + return false + }) + + // replace the outdated traces with the new trace information + for _, nt := range newTraces { + m.keeper.SetDenomTrace(ctx, nt) + } + return nil +} + +func equalTraces(dtA, dtB types.DenomTrace) bool { + return dtA.BaseDenom == dtB.BaseDenom && dtA.Path == dtB.Path +} diff --git a/modules/apps/transfer/keeper/migrations_test.go b/modules/apps/transfer/keeper/migrations_test.go new file mode 100644 index 00000000000..ff7da8ca089 --- /dev/null +++ b/modules/apps/transfer/keeper/migrations_test.go @@ -0,0 +1,123 @@ +package keeper_test + +import ( + "fmt" + + transferkeeper "github.com/cosmos/ibc-go/v4/modules/apps/transfer/keeper" + transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" +) + +func (suite *KeeperTestSuite) TestMigratorMigrateTraces() { + + testCases := []struct { + msg string + malleate func() + expectedTraces transfertypes.Traces + }{ + + { + "success: two slashes in base denom", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace( + suite.chainA.GetContext(), + transfertypes.DenomTrace{ + BaseDenom: "pool/1", Path: "transfer/channel-0/gamm", + }) + }, + transfertypes.Traces{ + { + BaseDenom: "gamm/pool/1", Path: "transfer/channel-0", + }, + }, + }, + { + "success: one slash in base denom", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace( + suite.chainA.GetContext(), + transfertypes.DenomTrace{ + BaseDenom: "0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", Path: "transfer/channel-149/erc", + }) + }, + transfertypes.Traces{ + { + BaseDenom: "erc/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", Path: "transfer/channel-149", + }, + }, + }, + { + "success: multiple slashes in a row in base denom", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace( + suite.chainA.GetContext(), + transfertypes.DenomTrace{ + BaseDenom: "1", Path: "transfer/channel-5/gamm//pool", + }) + }, + transfertypes.Traces{ + { + BaseDenom: "gamm//pool/1", Path: "transfer/channel-5", + }, + }, + }, + { + "success: multihop base denom", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace( + suite.chainA.GetContext(), + transfertypes.DenomTrace{ + BaseDenom: "transfer/channel-1/uatom", Path: "transfer/channel-0", + }) + }, + transfertypes.Traces{ + { + BaseDenom: "uatom", Path: "transfer/channel-0/transfer/channel-1", + }, + }, + }, + { + "success: non-standard port", + func() { + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace( + suite.chainA.GetContext(), + transfertypes.DenomTrace{ + BaseDenom: "customport/channel-7/uatom", Path: "transfer/channel-0/transfer/channel-1", + }) + }, + transfertypes.Traces{ + { + BaseDenom: "uatom", Path: "transfer/channel-0/transfer/channel-1/customport/channel-7", + }, + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() // explicitly set up denom traces + + migrator := transferkeeper.NewMigrator(suite.chainA.GetSimApp().TransferKeeper) + err := migrator.MigrateTraces(suite.chainA.GetContext()) + suite.Require().NoError(err) + + traces := suite.chainA.GetSimApp().TransferKeeper.GetAllDenomTraces(suite.chainA.GetContext()) + suite.Require().Equal(tc.expectedTraces, traces) + }) + } +} + +func (suite *KeeperTestSuite) TestMigratorMigrateTracesCorruptionDetection() { + // IBCDenom() previously would return "customport/channel-0/uatom", but now should return ibc/{hash} + corruptedDenomTrace := transfertypes.DenomTrace{ + BaseDenom: "customport/channel-0/uatom", + Path: "", + } + suite.chainA.GetSimApp().TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), corruptedDenomTrace) + + migrator := transferkeeper.NewMigrator(suite.chainA.GetSimApp().TransferKeeper) + suite.Panics(func() { + migrator.MigrateTraces(suite.chainA.GetContext()) + }) +} diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index 14773532418..5a892ea8a79 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -10,8 +10,6 @@ import ( var _ types.MsgServer = Keeper{} -// See createOutgoingPacket in spec:https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay - // Transfer defines a rpc handler method for MsgTransfer. func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -20,9 +18,11 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. if err != nil { return nil, err } - if err := k.SendTransfer( + + sequence, err := k.sendTransfer( ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, - ); err != nil { + msg.Memo) + if err != nil { return nil, err } @@ -40,5 +40,5 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. ), }) - return &types.MsgTransferResponse{}, nil + return &types.MsgTransferResponse{Sequence: sequence}, nil } diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go new file mode 100644 index 00000000000..261a98ecf66 --- /dev/null +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -0,0 +1,96 @@ +package keeper_test + +import ( + sdk "github.com/Finschia/finschia-sdk/types" + banktypes "github.com/Finschia/finschia-sdk/x/bank/types" + + "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" +) + +func (suite *KeeperTestSuite) TestMsgTransfer() { + var msg *types.MsgTransfer + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "bank send enabled for denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: true}}, + }, + ) + }, + true, + }, + { + "invalid sender", + func() { + msg.Sender = "address" + }, + false, + }, + { + "sender is a blocked address", + func() { + msg.Sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName).String() + }, + false, + }, + { + "bank send disabled for denom", + func() { + suite.chainA.GetSimApp().BankKeeper.SetParams(suite.chainA.GetContext(), + banktypes.Params{ + SendEnabled: []*banktypes.SendEnabled{{Denom: sdk.DefaultBondDenom, Enabled: false}}, + }, + ) + }, + false, + }, + { + "channel does not exist", + func() { + msg.SourceChannel = "channel-100" + }, + false, + }, + } + + for _, tc := range testCases { + suite.SetupTest() + + path := NewTransferPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + coin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + msg = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), 0, // only use timeout height + ) + msg.Memo = "memo" + + tc.malleate() + + res, err := suite.chainA.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NotEqual(res.Sequence, uint64(0)) + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + } +} diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 250df8b7f07..e63e899ab29 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -48,6 +48,8 @@ import ( // 4. A -> C : sender chain is sink zone. Denom upon receiving: 'C/B/denom' // 5. C -> B : sender chain is sink zone. Denom upon receiving: 'B/denom' // 6. B -> A : sender chain is sink zone. Denom upon receiving: 'denom' +// +// Note: An IBC Transfer must be initiated using a MsgTransfer via the Transfer rpc handler func (k Keeper) SendTransfer( ctx sdk.Context, sourcePort, @@ -58,13 +60,47 @@ func (k Keeper) SendTransfer( timeoutHeight clienttypes.Height, timeoutTimestamp uint64, ) error { + _, err := k.sendTransfer( + ctx, + sourcePort, + sourceChannel, + token, + sender, + receiver, + timeoutHeight, + timeoutTimestamp, + "", + ) + return err +} + +// sendTransfer handles transfer sending logic. +func (k Keeper) sendTransfer( + ctx sdk.Context, + sourcePort, + sourceChannel string, + token sdk.Coin, + sender sdk.AccAddress, + receiver string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + memo string, +) (uint64, error) { if !k.GetSendEnabled(ctx) { - return types.ErrSendDisabled + return 0, types.ErrSendDisabled + } + + if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { + return 0, sdkerrors.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", token.Denom) + } + + if k.bankKeeper.BlockedAddr(sender) { + return 0, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to send funds", sender) } sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) if !found { - return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel) + return 0, sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel) } destinationPort := sourceChannelEnd.GetCounterparty().GetPortID() @@ -73,7 +109,7 @@ func (k Keeper) SendTransfer( // get the next sequence sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) if !found { - return sdkerrors.Wrapf( + return 0, sdkerrors.Wrapf( channeltypes.ErrSequenceSendNotFound, "source port: %s, source channel: %s", sourcePort, sourceChannel, ) @@ -83,7 +119,7 @@ func (k Keeper) SendTransfer( // See spec for this logic: https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel)) if !ok { - return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") + return 0, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic @@ -96,7 +132,7 @@ func (k Keeper) SendTransfer( if strings.HasPrefix(token.Denom, "ibc/") { fullDenomPath, err = k.DenomPathFromHash(ctx, token.Denom) if err != nil { - return err + return 0, err } } @@ -119,7 +155,7 @@ func (k Keeper) SendTransfer( if err := k.bankKeeper.SendCoins( ctx, sender, escrowAddress, sdk.NewCoins(token), ); err != nil { - return err + return 0, err } } else { @@ -129,7 +165,7 @@ func (k Keeper) SendTransfer( if err := k.bankKeeper.SendCoinsFromAccountToModule( ctx, sender, types.ModuleName, sdk.NewCoins(token), ); err != nil { - return err + return 0, err } if err := k.bankKeeper.BurnCoins( @@ -145,6 +181,7 @@ func (k Keeper) SendTransfer( packetData := types.NewFungibleTokenPacketData( fullDenomPath, token.Amount.String(), sender.String(), receiver, ) + packetData.Memo = memo packet := channeltypes.NewPacket( packetData.GetBytes(), @@ -158,7 +195,7 @@ func (k Keeper) SendTransfer( ) if err := k.ics4Wrapper.SendPacket(ctx, channelCap, packet); err != nil { - return err + return 0, err } defer func() { @@ -177,7 +214,7 @@ func (k Keeper) SendTransfer( ) }() - return nil + return sequence, nil } // OnRecvPacket processes a cross chain fungible token transfer. If the diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index e40c39fc4a3..4b1d57700b7 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -19,6 +19,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { var ( amount sdk.Coin path *ibctesting.Path + sender sdk.AccAddress err error ) @@ -67,7 +68,14 @@ func (suite *KeeperTestSuite) TestSendTransfer() { amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) }, true, false, }, - + { + "transfer failed - sender account is blocked", + func() { + suite.coordinator.CreateTransferChannels(path) + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName) + }, true, false, + }, // createOutgoingPacket tests // - source chain { @@ -105,6 +113,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { suite.SetupTest() // reset path = NewTransferPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) + sender = suite.chainA.SenderAccount.GetAddress() tc.malleate() @@ -132,7 +141,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer( suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount, - suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0, + sender, suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, ) if tc.expPass { @@ -153,6 +162,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { trace types.DenomTrace amount sdk.Int receiver string + memo string ) testCases := []struct { @@ -162,7 +172,13 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { expPass bool }{ {"success receive on source chain", func() {}, true, true}, + {"success receive on source chain with memo", func() { + memo = "memo" + }, true, true}, {"success receive with coin from another chain as source", func() {}, false, true}, + {"success receive with coin from another chain as source with memo", func() { + memo = "memo" + }, false, true}, {"empty coin", func() { trace = types.DenomTrace{} amount = sdk.ZeroInt() @@ -203,6 +219,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { suite.coordinator.Setup(path) receiver = suite.chainB.SenderAccount.GetAddress().String() // must be explicitly changed in malleate + memo = "" // can be explicitly changed in malleate amount = sdk.NewInt(100) // must be explicitly changed in malleate seq := uint64(1) @@ -229,12 +246,14 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { // send coin from chainA to chainB transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(0, 110), 0) + transferMsg.Memo = memo _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed tc.malleate() data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver) + data.Memo = memo packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) diff --git a/modules/apps/transfer/module.go b/modules/apps/transfer/module.go index 4f7d3a050bb..562c20ad430 100644 --- a/modules/apps/transfer/module.go +++ b/modules/apps/transfer/module.go @@ -121,6 +121,11 @@ func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), am.keeper) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + + m := keeper.NewMigrator(am.keeper) + if err := cfg.RegisterMigration(types.ModuleName, 1, m.MigrateTraces); err != nil { + panic(fmt.Sprintf("failed to migrate transfer app from version 1 to 2: %v", err)) + } } // InitGenesis performs genesis initialization for the ibc-transfer module. It returns @@ -140,7 +145,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx sdk.Context, req ocabci.RequestBeginBlock) { diff --git a/modules/apps/transfer/types/codec.go b/modules/apps/transfer/types/codec.go index e9e5becc5b2..b7b866c9a10 100644 --- a/modules/apps/transfer/types/codec.go +++ b/modules/apps/transfer/types/codec.go @@ -1,10 +1,14 @@ package types import ( + "bytes" + "github.com/Finschia/finschia-sdk/codec" codectypes "github.com/Finschia/finschia-sdk/codec/types" sdk "github.com/Finschia/finschia-sdk/types" "github.com/Finschia/finschia-sdk/types/msgservice" + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" ) // RegisterLegacyAminoCodec registers the necessary x/ibc transfer interfaces and concrete types @@ -39,3 +43,32 @@ func init() { RegisterLegacyAminoCodec(amino) amino.Seal() } + +// mustProtoMarshalJSON provides an auxiliary function to return Proto3 JSON encoded +// bytes of a message. +// NOTE: Copied from https://github.com/cosmos/cosmos-sdk/blob/971c542453e0972ef1dfc5a80159ad5049c7211c/codec/json.go +// and modified in order to allow `EmitDefaults` to be set to false for ics20 packet marshalling. +// This allows for the introduction of the memo field to be backwards compatible. +func mustProtoMarshalJSON(msg proto.Message) []byte { + anyResolver := codectypes.NewInterfaceRegistry() + + // EmitDefaults is set to false to prevent marshalling of unpopulated fields (memo) + // OrigName and the anyResovler match the fields the original SDK function would expect + // in order to minimize changes. + + // OrigName is true since there is no particular reason to use camel case + // The any resolver is empty, but provided anyways. + jm := &jsonpb.Marshaler{OrigName: true, EmitDefaults: false, AnyResolver: anyResolver} + + err := codectypes.UnpackInterfaces(msg, codectypes.ProtoJSONPacker{JSONPBMarshaler: jm}) + if err != nil { + panic(err) + } + + buf := new(bytes.Buffer) + if err := jm.Marshal(buf, msg); err != nil { + panic(err) + } + + return buf.Bytes() +} diff --git a/modules/apps/transfer/types/codec_test.go b/modules/apps/transfer/types/codec_test.go new file mode 100644 index 00000000000..98d26a71255 --- /dev/null +++ b/modules/apps/transfer/types/codec_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "strings" + + sdk "github.com/Finschia/finschia-sdk/types" + + "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" +) + +// TestMustMarshalProtoJSON tests that the memo field is only emitted (marshalled) if it is populated +func (suite *TypesTestSuite) TestMustMarshalProtoJSON() { + memo := "memo" + packetData := types.NewFungibleTokenPacketData(sdk.DefaultBondDenom, "1", suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) + packetData.Memo = memo + + bz := packetData.GetBytes() + exists := strings.Contains(string(bz), memo) + suite.Require().True(exists) + + packetData.Memo = "" + + bz = packetData.GetBytes() + exists = strings.Contains(string(bz), memo) + suite.Require().False(exists) +} diff --git a/modules/apps/transfer/types/events.go b/modules/apps/transfer/types/events.go index a3ed5b413c7..89964a8a9a4 100644 --- a/modules/apps/transfer/types/events.go +++ b/modules/apps/transfer/types/events.go @@ -18,4 +18,5 @@ const ( AttributeKeyAck = "acknowledgement" AttributeKeyAckError = "error" AttributeKeyTraceHash = "trace_hash" + AttributeKeyMemo = "memo" ) diff --git a/modules/apps/transfer/types/expected_keepers.go b/modules/apps/transfer/types/expected_keepers.go index b235df5eb59..62001b6de07 100644 --- a/modules/apps/transfer/types/expected_keepers.go +++ b/modules/apps/transfer/types/expected_keepers.go @@ -24,6 +24,7 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error BlockedAddr(addr sdk.AccAddress) bool + IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool } // ICS4Wrapper defines the expected ICS4Wrapper for middleware diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index d5e45e6f210..f9883f05ecd 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -56,5 +56,5 @@ func (ftpd FungibleTokenPacketData) ValidateBasic() error { // GetBytes is a helper for serialising func (ftpd FungibleTokenPacketData) GetBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ftpd)) + return sdk.MustSortJSON(mustProtoMarshalJSON(&ftpd)) } diff --git a/modules/apps/transfer/types/packet.pb.go b/modules/apps/transfer/types/packet.pb.go index ca0ffa46e9e..b6f88395c7f 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -34,6 +34,8 @@ type FungibleTokenPacketData struct { Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` // the recipient address on the destination chain Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"` + // optional memo + Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` } func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} } @@ -97,6 +99,13 @@ func (m *FungibleTokenPacketData) GetReceiver() string { return "" } +func (m *FungibleTokenPacketData) GetMemo() string { + if m != nil { + return m.Memo + } + return "" +} + func init() { proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketData") } @@ -106,23 +115,23 @@ func init() { } var fileDescriptor_653ca2ce9a5ca313 = []byte{ - // 242 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xbd, 0x4a, 0x04, 0x31, - 0x14, 0x46, 0x27, 0xfe, 0x2c, 0x9a, 0x72, 0x10, 0x1d, 0x44, 0x82, 0x58, 0x69, 0x61, 0x02, 0xab, - 0x60, 0x2f, 0x62, 0xad, 0x62, 0x65, 0x97, 0x64, 0xae, 0x63, 0xd8, 0x49, 0x6e, 0x48, 0x32, 0x03, - 0xe2, 0x4b, 0xf8, 0x58, 0x96, 0x5b, 0x5a, 0xca, 0xcc, 0x8b, 0xc8, 0x66, 0x74, 0xd9, 0xf2, 0x9c, - 0xfb, 0xdd, 0xe2, 0xd0, 0x0b, 0xa3, 0xb4, 0x90, 0xde, 0xb7, 0x46, 0xcb, 0x64, 0xd0, 0x45, 0x91, - 0x82, 0x74, 0xf1, 0x15, 0x82, 0xe8, 0xe7, 0xc2, 0x4b, 0xbd, 0x80, 0xc4, 0x7d, 0xc0, 0x84, 0xe5, - 0x89, 0x51, 0x9a, 0x6f, 0x4e, 0xf9, 0xff, 0x94, 0xf7, 0xf3, 0xb3, 0x0f, 0x7a, 0x74, 0xdf, 0xb9, - 0xc6, 0xa8, 0x16, 0x9e, 0x71, 0x01, 0xee, 0x21, 0xbf, 0xde, 0xc9, 0x24, 0xcb, 0x03, 0xba, 0x5b, - 0x83, 0x43, 0x5b, 0x91, 0x53, 0x72, 0xbe, 0xff, 0x34, 0x41, 0x79, 0x48, 0x67, 0xd2, 0x62, 0xe7, - 0x52, 0xb5, 0x95, 0xf5, 0x1f, 0xad, 0x7c, 0x04, 0x57, 0x43, 0xa8, 0xb6, 0x27, 0x3f, 0x51, 0x79, - 0x4c, 0xf7, 0x02, 0x68, 0x30, 0x3d, 0x84, 0x6a, 0x27, 0x5f, 0xd6, 0x7c, 0xfb, 0xf8, 0x35, 0x30, - 0xb2, 0x1c, 0x18, 0xf9, 0x19, 0x18, 0xf9, 0x1c, 0x59, 0xb1, 0x1c, 0x59, 0xf1, 0x3d, 0xb2, 0xe2, - 0xe5, 0xa6, 0x31, 0xe9, 0xad, 0x53, 0x5c, 0xa3, 0x15, 0x1a, 0xa3, 0xc5, 0x28, 0x8c, 0xd2, 0x97, - 0x0d, 0x8a, 0xfe, 0x5a, 0x58, 0xac, 0xbb, 0x16, 0xe2, 0xaa, 0x7f, 0xa3, 0x3b, 0xbd, 0x7b, 0x88, - 0x6a, 0x96, 0xa3, 0xaf, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x0b, 0x30, 0xf2, 0x21, 0x01, - 0x00, 0x00, + // 254 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x4a, 0x34, 0x31, + 0x14, 0x46, 0x27, 0xff, 0xbf, 0xbb, 0x68, 0xca, 0x20, 0x3a, 0x88, 0x04, 0xb1, 0xd2, 0xc2, 0x09, + 0xac, 0x82, 0xbd, 0x88, 0xb5, 0x8a, 0x95, 0x5d, 0x92, 0xb9, 0x8e, 0x61, 0x27, 0xb9, 0x21, 0xc9, + 0x0c, 0xf8, 0x14, 0xfa, 0x58, 0x96, 0x5b, 0x5a, 0xca, 0xcc, 0x8b, 0xc8, 0x66, 0x54, 0xb6, 0xcb, + 0x39, 0xf9, 0x6e, 0x73, 0xe8, 0x99, 0x51, 0x5a, 0x48, 0xef, 0x5b, 0xa3, 0x65, 0x32, 0xe8, 0xa2, + 0x48, 0x41, 0xba, 0xf8, 0x0c, 0x41, 0xf4, 0x4b, 0xe1, 0xa5, 0x5e, 0x41, 0xaa, 0x7c, 0xc0, 0x84, + 0xec, 0xc8, 0x28, 0x5d, 0x6d, 0x4f, 0xab, 0xdf, 0x69, 0xd5, 0x2f, 0x4f, 0xde, 0x08, 0x3d, 0xb8, + 0xed, 0x5c, 0x63, 0x54, 0x0b, 0x8f, 0xb8, 0x02, 0x77, 0x97, 0x6f, 0x6f, 0x64, 0x92, 0x6c, 0x8f, + 0xce, 0x6b, 0x70, 0x68, 0x4b, 0x72, 0x4c, 0x4e, 0x77, 0x1f, 0x26, 0x60, 0xfb, 0x74, 0x21, 0x2d, + 0x76, 0x2e, 0x95, 0xff, 0xb2, 0xfe, 0xa1, 0x8d, 0x8f, 0xe0, 0x6a, 0x08, 0xe5, 0xff, 0xc9, 0x4f, + 0xc4, 0x0e, 0xe9, 0x4e, 0x00, 0x0d, 0xa6, 0x87, 0x50, 0xce, 0xf2, 0xcf, 0x1f, 0x33, 0x46, 0x67, + 0x16, 0x2c, 0x96, 0xf3, 0xec, 0xf3, 0xfb, 0xfa, 0xfe, 0x63, 0xe0, 0x64, 0x3d, 0x70, 0xf2, 0x35, + 0x70, 0xf2, 0x3e, 0xf2, 0x62, 0x3d, 0xf2, 0xe2, 0x73, 0xe4, 0xc5, 0xd3, 0x55, 0x63, 0xd2, 0x4b, + 0xa7, 0x2a, 0x8d, 0x56, 0x68, 0x8c, 0x16, 0xa3, 0x30, 0x4a, 0x9f, 0x37, 0x28, 0xfa, 0x4b, 0x61, + 0xb1, 0xee, 0x5a, 0x88, 0x9b, 0x28, 0x5b, 0x31, 0xd2, 0xab, 0x87, 0xa8, 0x16, 0xb9, 0xc4, 0xc5, + 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x52, 0xc6, 0x33, 0x84, 0x36, 0x01, 0x00, 0x00, } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -145,6 +154,13 @@ func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintPacket(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x2a + } if len(m.Receiver) > 0 { i -= len(m.Receiver) copy(dAtA[i:], m.Receiver) @@ -209,6 +225,10 @@ func (m *FungibleTokenPacketData) Size() (n int) { if l > 0 { n += 1 + l + sovPacket(uint64(l)) } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } return n } @@ -375,6 +395,38 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error { } m.Receiver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPacket(dAtA[iNdEx:]) diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 709b6c62a2f..b8149129167 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -12,6 +12,7 @@ import ( tmbytes "github.com/Finschia/ostracon/libs/bytes" tmtypes "github.com/Finschia/ostracon/types" + channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v4/modules/core/24-host" ) @@ -20,9 +21,9 @@ import ( // // Examples: // -// - "transfer/channelidone/uatom" => DenomTrace{Path: "transfer/channelidone", BaseDenom: "uatom"} -// - "transfer/channelidone/transfer/channelidtwo/uatom" => DenomTrace{Path: "transfer/channelidone/transfer/channelidtwo", BaseDenom: "uatom"} -// - "transfer/channelidone/gamm/pool/1" => DenomTrace{Path: "transfer/channelidone", BaseDenom: "gamm/pool/1"} +// - "portidone/channel-0/uatom" => DenomTrace{Path: "portidone/channel-0", BaseDenom: "uatom"} +// - "portidone/channel-0/portidtwo/channel-1/uatom" => DenomTrace{Path: "portidone/channel-0/portidtwo/channel-1", BaseDenom: "uatom"} +// - "portidone/channel-0/gamm/pool/1" => DenomTrace{Path: "portidone/channel-0", BaseDenom: "gamm/pool/1"} // - "gamm/pool/1" => DenomTrace{Path: "", BaseDenom: "gamm/pool/1"} // - "uatom" => DenomTrace{Path: "", BaseDenom: "uatom"} func ParseDenomTrace(rawDenom string) DenomTrace { @@ -77,11 +78,23 @@ func (dt DenomTrace) GetFullDenomPath() string { // extractPathAndBaseFromFullDenom returns the trace path and the base denom from // the elements that constitute the complete denom. func extractPathAndBaseFromFullDenom(fullDenomItems []string) (string, string) { - var path []string - var baseDenom []string + var ( + path []string + baseDenom []string + ) + length := len(fullDenomItems) for i := 0; i < length; i = i + 2 { - if i < length-1 && length > 2 && fullDenomItems[i] == PortID { + // The IBC specification does not guarentee the expected format of the + // destination port or destination channel identifier. A short term solution + // to determine base denomination is to expect the channel identifier to be the + // one ibc-go specifies. A longer term solution is to separate the path and base + // denomination in the ICS20 packet. If an intermediate hop prefixes the full denom + // with a channel identifier format different from our own, the base denomination + // will be incorrectly parsed, but the token will continue to be treated correctly + // as an IBC denomination. The hash used to store the token internally on our chain + // will be the same value as the base denomination being correctly parsed. + if i < length-1 && length > 2 && channeltypes.IsValidChannelID(fullDenomItems[i+1]) { path = append(path, fullDenomItems[i], fullDenomItems[i+1]) } else { baseDenom = fullDenomItems[i:] @@ -165,7 +178,7 @@ func (t Traces) Sort() Traces { // ValidatePrefixedDenom checks that the denomination for an IBC fungible token packet denom is correctly prefixed. // The function will return no error if the given string follows one of the two formats: // -// - Prefixed denomination: 'transfer/{channelIDN}/.../transfer/{channelID0}/baseDenom' +// - Prefixed denomination: '{portIDN}/{channelIDN}/.../{portID0}/{channelID0}/baseDenom' // - Unprefixed denomination: 'baseDenom' // // 'baseDenom' may or may not contain '/'s diff --git a/modules/apps/transfer/types/trace_test.go b/modules/apps/transfer/types/trace_test.go index 6526cbd952d..ba0690423bd 100644 --- a/modules/apps/transfer/types/trace_test.go +++ b/modules/apps/transfer/types/trace_test.go @@ -17,20 +17,23 @@ func TestParseDenomTrace(t *testing.T) { {"base denom ending with '/'", "uatom/", DenomTrace{BaseDenom: "uatom/"}}, {"base denom with single '/'s", "gamm/pool/1", DenomTrace{BaseDenom: "gamm/pool/1"}}, {"base denom with double '/'s", "gamm//pool//1", DenomTrace{BaseDenom: "gamm//pool//1"}}, - {"trace info", "transfer/channelToA/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}}, - {"trace info with base denom ending in '/'", "transfer/channelToA/uatom/", DenomTrace{BaseDenom: "uatom/", Path: "transfer/channelToA"}}, - {"trace info with single '/' in base denom", "transfer/channelToA/erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", DenomTrace{BaseDenom: "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", Path: "transfer/channelToA"}}, - {"trace info with multiple '/'s in base denom", "transfer/channelToA/gamm/pool/1", DenomTrace{BaseDenom: "gamm/pool/1", Path: "transfer/channelToA"}}, - {"trace info with multiple double '/'s in base denom", "transfer/channelToA/gamm//pool//1", DenomTrace{BaseDenom: "gamm//pool//1", Path: "transfer/channelToA"}}, - {"trace info with multiple port/channel pairs", "transfer/channelToA/transfer/channelToB/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}}, + {"trace info", "transfer/channel-1/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}}, + {"trace info with custom port", "customtransfer/channel-1/uatom", DenomTrace{BaseDenom: "uatom", Path: "customtransfer/channel-1"}}, + {"trace info with base denom ending in '/'", "transfer/channel-1/uatom/", DenomTrace{BaseDenom: "uatom/", Path: "transfer/channel-1"}}, + {"trace info with single '/' in base denom", "transfer/channel-1/erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", DenomTrace{BaseDenom: "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", Path: "transfer/channel-1"}}, + {"trace info with multiple '/'s in base denom", "transfer/channel-1/gamm/pool/1", DenomTrace{BaseDenom: "gamm/pool/1", Path: "transfer/channel-1"}}, + {"trace info with multiple double '/'s in base denom", "transfer/channel-1/gamm//pool//1", DenomTrace{BaseDenom: "gamm//pool//1", Path: "transfer/channel-1"}}, + {"trace info with multiple port/channel pairs", "transfer/channel-1/transfer/channel-2/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}}, + {"trace info with multiple custom ports", "customtransfer/channel-1/alternativetransfer/channel-2/uatom", DenomTrace{BaseDenom: "uatom", Path: "customtransfer/channel-1/alternativetransfer/channel-2"}}, {"incomplete path", "transfer/uatom", DenomTrace{BaseDenom: "transfer/uatom"}}, - {"invalid path (1)", "transfer//uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/"}}, - {"invalid path (2)", "channelToA/transfer/uatom", DenomTrace{BaseDenom: "channelToA/transfer/uatom"}}, + {"invalid path (1)", "transfer//uatom", DenomTrace{BaseDenom: "transfer//uatom", Path: ""}}, + {"invalid path (2)", "channel-1/transfer/uatom", DenomTrace{BaseDenom: "channel-1/transfer/uatom"}}, {"invalid path (3)", "uatom/transfer", DenomTrace{BaseDenom: "uatom/transfer"}}, - {"invalid path (4)", "transfer/channelToA", DenomTrace{BaseDenom: "transfer/channelToA"}}, - {"invalid path (5)", "transfer/channelToA/", DenomTrace{Path: "transfer/channelToA"}}, - {"invalid path (6)", "transfer/channelToA/transfer", DenomTrace{BaseDenom: "transfer", Path: "transfer/channelToA"}}, - {"invalid path (7)", "transfer/channelToA/transfer/channelToB", DenomTrace{Path: "transfer/channelToA/transfer/channelToB"}}, + {"invalid path (4)", "transfer/channel-1", DenomTrace{BaseDenom: "transfer/channel-1"}}, + {"invalid path (5)", "transfer/channel-1/", DenomTrace{Path: "transfer/channel-1"}}, + {"invalid path (6)", "transfer/channel-1/transfer", DenomTrace{BaseDenom: "transfer", Path: "transfer/channel-1"}}, + {"invalid path (7)", "transfer/channel-1/transfer/channel-2", DenomTrace{Path: "transfer/channel-1/transfer/channel-2"}}, + {"invalid path (8)", "transfer/channelToA/uatom", DenomTrace{BaseDenom: "transfer/channelToA/uatom", Path: ""}}, } for _, tc := range testCases { @@ -46,7 +49,7 @@ func TestDenomTrace_IBCDenom(t *testing.T) { expDenom string }{ {"base denom", DenomTrace{BaseDenom: "uatom"}, "uatom"}, - {"trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}, "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2"}, + {"trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}, "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9"}, } for _, tc := range testCases { @@ -65,12 +68,12 @@ func TestDenomTrace_Validate(t *testing.T) { {"base denom only with single '/'", DenomTrace{BaseDenom: "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA"}, false}, {"base denom only with multiple '/'s", DenomTrace{BaseDenom: "gamm/pool/1"}, false}, {"empty DenomTrace", DenomTrace{}, true}, - {"valid single trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}, false}, - {"valid multiple trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, false}, + {"valid single trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1"}, false}, + {"valid multiple trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}, false}, {"single trace identifier", DenomTrace{BaseDenom: "uatom", Path: "transfer"}, true}, - {"invalid port ID", DenomTrace{BaseDenom: "uatom", Path: "(transfer)/channelToA"}, true}, - {"invalid channel ID", DenomTrace{BaseDenom: "uatom", Path: "transfer/(channelToA)"}, true}, - {"empty base denom with trace", DenomTrace{BaseDenom: "", Path: "transfer/channelToA"}, true}, + {"invalid port ID", DenomTrace{BaseDenom: "uatom", Path: "(transfer)/channel-1"}, true}, + {"invalid channel ID", DenomTrace{BaseDenom: "uatom", Path: "transfer/(channel-1)"}, true}, + {"empty base denom with trace", DenomTrace{BaseDenom: "", Path: "transfer/channel-1"}, true}, } for _, tc := range testCases { @@ -90,16 +93,16 @@ func TestTraces_Validate(t *testing.T) { expError bool }{ {"empty Traces", Traces{}, false}, - {"valid multiple trace info", Traces{{BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}}, false}, + {"valid multiple trace info", Traces{{BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}}, false}, { "valid multiple trace info", Traces{ - {BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, - {BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, + {BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}, + {BaseDenom: "uatom", Path: "transfer/channel-1/transfer/channel-2"}, }, true, }, - {"empty base denom with trace", Traces{{BaseDenom: "", Path: "transfer/channelToA"}}, true}, + {"empty base denom with trace", Traces{{BaseDenom: "", Path: "transfer/channel-1"}}, true}, } for _, tc := range testCases { @@ -118,26 +121,25 @@ func TestValidatePrefixedDenom(t *testing.T) { denom string expError bool }{ - {"prefixed denom", "transfer/channelToA/uatom", false}, - {"prefixed denom with '/'", "transfer/channelToA/gamm/pool/1", false}, + {"prefixed denom", "transfer/channel-1/uatom", false}, + {"prefixed denom with '/'", "transfer/channel-1/gamm/pool/1", false}, {"empty prefix", "/uatom", false}, {"empty identifiers", "//uatom", false}, {"base denom", "uatom", false}, {"base denom with single '/'", "erc20/0x85bcBCd7e79Ec36f4fBBDc54F90C643d921151AA", false}, {"base denom with multiple '/'s", "gamm/pool/1", false}, - {"invalid port ID", "(transfer)/channelToA/uatom", false}, + {"invalid port ID", "(transfer)/channel-1/uatom", true}, {"empty denom", "", true}, {"single trace identifier", "transfer/", true}, - {"invalid channel ID", "transfer/(channelToA)/uatom", true}, } for _, tc := range testCases { err := ValidatePrefixedDenom(tc.denom) if tc.expError { require.Error(t, err, tc.name) - continue + } else { + require.NoError(t, err, tc.name) } - require.NoError(t, err, tc.name) } } diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index 6d3c210450e..8a565fad86b 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -50,6 +50,8 @@ type MsgTransfer struct { // Timeout timestamp in absolute nanoseconds since unix epoch. // The timeout is disabled when set to 0. TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty" yaml:"timeout_timestamp"` + // optional memo + Memo string `protobuf:"bytes,8,opt,name=memo,proto3" json:"memo,omitempty"` } func (m *MsgTransfer) Reset() { *m = MsgTransfer{} } @@ -87,6 +89,8 @@ var xxx_messageInfo_MsgTransfer proto.InternalMessageInfo // MsgTransferResponse defines the Msg/Transfer response type. type MsgTransferResponse struct { + // sequence number of the transfer packet sent + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` } func (m *MsgTransferResponse) Reset() { *m = MsgTransferResponse{} } @@ -122,6 +126,13 @@ func (m *MsgTransferResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgTransferResponse proto.InternalMessageInfo +func (m *MsgTransferResponse) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + func init() { proto.RegisterType((*MsgTransfer)(nil), "ibc.applications.transfer.v1.MsgTransfer") proto.RegisterType((*MsgTransferResponse)(nil), "ibc.applications.transfer.v1.MsgTransferResponse") @@ -132,38 +143,40 @@ func init() { } var fileDescriptor_7401ed9bed2f8e09 = []byte{ - // 494 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0xc7, 0x6d, 0x92, 0x86, 0x70, 0x51, 0x2b, 0x30, 0xb4, 0x72, 0xa3, 0x62, 0x47, 0x96, 0x90, - 0xc2, 0xc0, 0x9d, 0x5c, 0x40, 0x95, 0x3a, 0xa1, 0x74, 0x81, 0xa1, 0x12, 0x58, 0x9d, 0x58, 0x8a, - 0x7d, 0x3d, 0x9c, 0x13, 0xf1, 0x3d, 0xeb, 0xee, 0x62, 0xd1, 0x6f, 0xc0, 0xc8, 0x47, 0xe8, 0xcc, - 0x27, 0xe9, 0xd8, 0x91, 0x29, 0x42, 0xc9, 0xc2, 0x9c, 0x4f, 0x80, 0xce, 0xbe, 0x84, 0x64, 0x41, - 0x4c, 0xf6, 0x7b, 0xff, 0xdf, 0xbb, 0xbf, 0xde, 0xbd, 0x77, 0xe8, 0x19, 0xcf, 0x28, 0x49, 0xcb, - 0x72, 0xc2, 0x69, 0xaa, 0x39, 0x08, 0x45, 0xb4, 0x4c, 0x85, 0xfa, 0xcc, 0x24, 0xa9, 0x62, 0xa2, - 0xbf, 0xe2, 0x52, 0x82, 0x06, 0xef, 0x88, 0x67, 0x14, 0x6f, 0x62, 0x78, 0x85, 0xe1, 0x2a, 0xee, - 0x3f, 0xc9, 0x21, 0x87, 0x1a, 0x24, 0xe6, 0xaf, 0xa9, 0xe9, 0x07, 0x14, 0x54, 0x01, 0x8a, 0x64, - 0xa9, 0x62, 0xa4, 0x8a, 0x33, 0xa6, 0xd3, 0x98, 0x50, 0xe0, 0xc2, 0xea, 0xa1, 0xb1, 0xa6, 0x20, - 0x19, 0xa1, 0x13, 0xce, 0x84, 0x36, 0x86, 0xcd, 0x5f, 0x03, 0x44, 0x3f, 0x5a, 0xa8, 0x77, 0xae, - 0xf2, 0x0b, 0xeb, 0xe4, 0x9d, 0xa0, 0x9e, 0x82, 0xa9, 0xa4, 0xec, 0xb2, 0x04, 0xa9, 0x7d, 0x77, - 0xe0, 0x0e, 0x1f, 0x8c, 0x0e, 0x96, 0xb3, 0xd0, 0xbb, 0x4e, 0x8b, 0xc9, 0x69, 0xb4, 0x21, 0x46, - 0x09, 0x6a, 0xa2, 0xf7, 0x20, 0xb5, 0xf7, 0x06, 0xed, 0x59, 0x8d, 0x8e, 0x53, 0x21, 0xd8, 0xc4, - 0xbf, 0x57, 0xd7, 0x1e, 0x2e, 0x67, 0xe1, 0xfe, 0x56, 0xad, 0xd5, 0xa3, 0x64, 0xb7, 0x49, 0x9c, - 0x35, 0xb1, 0xf7, 0x1a, 0xed, 0x68, 0xf8, 0xc2, 0x84, 0xdf, 0x1a, 0xb8, 0xc3, 0xde, 0xf1, 0x21, - 0x6e, 0x7a, 0xc3, 0xa6, 0x37, 0x6c, 0x7b, 0xc3, 0x67, 0xc0, 0xc5, 0xa8, 0x7d, 0x3b, 0x0b, 0x9d, - 0xa4, 0xa1, 0xbd, 0x03, 0xd4, 0x51, 0x4c, 0x5c, 0x31, 0xe9, 0xb7, 0x8d, 0x61, 0x62, 0x23, 0xaf, - 0x8f, 0xba, 0x92, 0x51, 0xc6, 0x2b, 0x26, 0xfd, 0x9d, 0x5a, 0x59, 0xc7, 0xde, 0x27, 0xb4, 0xa7, - 0x79, 0xc1, 0x60, 0xaa, 0x2f, 0xc7, 0x8c, 0xe7, 0x63, 0xed, 0x77, 0x6a, 0xcf, 0x3e, 0x36, 0x33, - 0x30, 0xf7, 0x85, 0xed, 0x2d, 0x55, 0x31, 0x7e, 0x5b, 0x13, 0xa3, 0xa7, 0xc6, 0xf4, 0x6f, 0x33, - 0xdb, 0xf5, 0x51, 0xb2, 0x6b, 0x13, 0x0d, 0xed, 0xbd, 0x43, 0x8f, 0x56, 0x84, 0xf9, 0x2a, 0x9d, - 0x16, 0xa5, 0x7f, 0x7f, 0xe0, 0x0e, 0xdb, 0xa3, 0xa3, 0xe5, 0x2c, 0xf4, 0xb7, 0x0f, 0x59, 0x23, - 0x51, 0xf2, 0xd0, 0xe6, 0x2e, 0x56, 0xa9, 0xd3, 0xee, 0xb7, 0x9b, 0xd0, 0xf9, 0x7d, 0x13, 0x3a, - 0xd1, 0x3e, 0x7a, 0xbc, 0x31, 0xab, 0x84, 0xa9, 0x12, 0x84, 0x62, 0xc7, 0x80, 0x5a, 0xe7, 0x2a, - 0xf7, 0xc6, 0xa8, 0xbb, 0x1e, 0xe3, 0x73, 0xfc, 0xaf, 0x65, 0xc2, 0x1b, 0xa7, 0xf4, 0xe3, 0xff, - 0x46, 0x57, 0x86, 0xa3, 0x0f, 0xb7, 0xf3, 0xc0, 0xbd, 0x9b, 0x07, 0xee, 0xaf, 0x79, 0xe0, 0x7e, - 0x5f, 0x04, 0xce, 0xdd, 0x22, 0x70, 0x7e, 0x2e, 0x02, 0xe7, 0xe3, 0x49, 0xce, 0xf5, 0x78, 0x9a, - 0x61, 0x0a, 0x05, 0xb1, 0xab, 0xc9, 0x33, 0xfa, 0x22, 0x07, 0x52, 0xbd, 0x22, 0x05, 0x5c, 0x4d, - 0x27, 0x4c, 0x99, 0xa7, 0xb0, 0xf1, 0x04, 0xf4, 0x75, 0xc9, 0x54, 0xd6, 0xa9, 0xd7, 0xf1, 0xe5, - 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x6e, 0x0e, 0x0d, 0x2c, 0x03, 0x00, 0x00, + // 516 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0xb5, 0x49, 0x1a, 0xc2, 0x46, 0xad, 0x60, 0x81, 0xca, 0x8d, 0x8a, 0x1d, 0x59, 0x42, 0x0a, + 0x07, 0x76, 0xe5, 0x02, 0xaa, 0xd4, 0x13, 0x4a, 0x2f, 0x70, 0xa8, 0x04, 0x56, 0x4f, 0x5c, 0x8a, + 0xbd, 0x1d, 0x9c, 0x15, 0xb1, 0xc7, 0x78, 0x37, 0x16, 0xfd, 0x03, 0x8e, 0x7c, 0x42, 0xbf, 0x84, + 0x73, 0x8f, 0x3d, 0x72, 0x8a, 0x50, 0x72, 0xe1, 0x9c, 0x2f, 0x40, 0x6b, 0x3b, 0x21, 0xb9, 0x20, + 0x4e, 0x9e, 0x99, 0xf7, 0xc6, 0x6f, 0xdf, 0xce, 0x2c, 0x79, 0x2a, 0x63, 0xc1, 0xa3, 0x3c, 0x9f, + 0x48, 0x11, 0x69, 0x89, 0x99, 0xe2, 0xba, 0x88, 0x32, 0xf5, 0x09, 0x0a, 0x5e, 0x06, 0x5c, 0x7f, + 0x65, 0x79, 0x81, 0x1a, 0xe9, 0xa1, 0x8c, 0x05, 0xdb, 0xa4, 0xb1, 0x15, 0x8d, 0x95, 0x41, 0xff, + 0x51, 0x82, 0x09, 0x56, 0x44, 0x6e, 0xa2, 0xba, 0xa7, 0xef, 0x0a, 0x54, 0x29, 0x2a, 0x1e, 0x47, + 0x0a, 0x78, 0x19, 0xc4, 0xa0, 0xa3, 0x80, 0x0b, 0x94, 0x59, 0x83, 0x7b, 0x46, 0x5a, 0x60, 0x01, + 0x5c, 0x4c, 0x24, 0x64, 0xda, 0x08, 0xd6, 0x51, 0x4d, 0xf0, 0x7f, 0xb4, 0x48, 0xef, 0x4c, 0x25, + 0xe7, 0x8d, 0x12, 0x3d, 0x26, 0x3d, 0x85, 0xd3, 0x42, 0xc0, 0x45, 0x8e, 0x85, 0x76, 0xec, 0x81, + 0x3d, 0xbc, 0x37, 0xda, 0x5f, 0xce, 0x3c, 0x7a, 0x15, 0xa5, 0x93, 0x13, 0x7f, 0x03, 0xf4, 0x43, + 0x52, 0x67, 0xef, 0xb0, 0xd0, 0xf4, 0x35, 0xd9, 0x6b, 0x30, 0x31, 0x8e, 0xb2, 0x0c, 0x26, 0xce, + 0x9d, 0xaa, 0xf7, 0x60, 0x39, 0xf3, 0x1e, 0x6f, 0xf5, 0x36, 0xb8, 0x1f, 0xee, 0xd6, 0x85, 0xd3, + 0x3a, 0xa7, 0xaf, 0xc8, 0x8e, 0xc6, 0xcf, 0x90, 0x39, 0xad, 0x81, 0x3d, 0xec, 0x1d, 0x1d, 0xb0, + 0xda, 0x1b, 0x33, 0xde, 0x58, 0xe3, 0x8d, 0x9d, 0xa2, 0xcc, 0x46, 0xed, 0x9b, 0x99, 0x67, 0x85, + 0x35, 0x9b, 0xee, 0x93, 0x8e, 0x82, 0xec, 0x12, 0x0a, 0xa7, 0x6d, 0x04, 0xc3, 0x26, 0xa3, 0x7d, + 0xd2, 0x2d, 0x40, 0x80, 0x2c, 0xa1, 0x70, 0x76, 0x2a, 0x64, 0x9d, 0xd3, 0x8f, 0x64, 0x4f, 0xcb, + 0x14, 0x70, 0xaa, 0x2f, 0xc6, 0x20, 0x93, 0xb1, 0x76, 0x3a, 0x95, 0x66, 0x9f, 0x99, 0x19, 0x98, + 0xfb, 0x62, 0xcd, 0x2d, 0x95, 0x01, 0x7b, 0x53, 0x31, 0x46, 0x4f, 0x8c, 0xe8, 0x5f, 0x33, 0xdb, + 0xfd, 0x7e, 0xb8, 0xdb, 0x14, 0x6a, 0x36, 0x7d, 0x4b, 0x1e, 0xac, 0x18, 0xe6, 0xab, 0x74, 0x94, + 0xe6, 0xce, 0xdd, 0x81, 0x3d, 0x6c, 0x8f, 0x0e, 0x97, 0x33, 0xcf, 0xd9, 0xfe, 0xc9, 0x9a, 0xe2, + 0x87, 0xf7, 0x9b, 0xda, 0xf9, 0xaa, 0x44, 0x29, 0x69, 0xa7, 0x90, 0xa2, 0xd3, 0xad, 0x4c, 0x54, + 0xf1, 0x49, 0xf7, 0xdb, 0xb5, 0x67, 0xfd, 0xbe, 0xf6, 0x2c, 0x3f, 0x20, 0x0f, 0x37, 0xe6, 0x17, + 0x82, 0xca, 0x31, 0x53, 0x60, 0xdc, 0x2b, 0xf8, 0x32, 0x85, 0x4c, 0x40, 0x35, 0xc4, 0x76, 0xb8, + 0xce, 0x8f, 0x90, 0xb4, 0xce, 0x54, 0x42, 0xc7, 0xa4, 0xbb, 0x1e, 0xfb, 0x33, 0xf6, 0xaf, 0xe5, + 0x63, 0x1b, 0x0a, 0xfd, 0xe0, 0xbf, 0xa9, 0xab, 0xc3, 0x8c, 0xde, 0xdf, 0xcc, 0x5d, 0xfb, 0x76, + 0xee, 0xda, 0xbf, 0xe6, 0xae, 0xfd, 0x7d, 0xe1, 0x5a, 0xb7, 0x0b, 0xd7, 0xfa, 0xb9, 0x70, 0xad, + 0x0f, 0xc7, 0x89, 0xd4, 0xe3, 0x69, 0xcc, 0x04, 0xa6, 0xbc, 0x59, 0x65, 0x19, 0x8b, 0xe7, 0x09, + 0xf2, 0xf2, 0x25, 0x4f, 0xf1, 0x72, 0x3a, 0x01, 0x65, 0x9e, 0xce, 0xc6, 0x93, 0xd1, 0x57, 0x39, + 0xa8, 0xb8, 0x53, 0xad, 0xef, 0x8b, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbe, 0x02, 0xa3, 0x4c, + 0x5c, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -268,6 +281,13 @@ func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintTx(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x42 + } if m.TimeoutTimestamp != 0 { i = encodeVarintTx(dAtA, i, uint64(m.TimeoutTimestamp)) i-- @@ -344,6 +364,11 @@ func (m *MsgTransferResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Sequence != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -387,6 +412,10 @@ func (m *MsgTransfer) Size() (n int) { if m.TimeoutTimestamp != 0 { n += 1 + sovTx(uint64(m.TimeoutTimestamp)) } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -396,6 +425,9 @@ func (m *MsgTransferResponse) Size() (n int) { } var l int _ = l + if m.Sequence != 0 { + n += 1 + sovTx(uint64(m.Sequence)) + } return n } @@ -647,6 +679,38 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error { break } } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -697,6 +761,25 @@ func (m *MsgTransferResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgTransferResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/modules/core/02-client/types/client.pb.go b/modules/core/02-client/types/client.pb.go index 045bd81cc44..024cea01697 100644 --- a/modules/core/02-client/types/client.pb.go +++ b/modules/core/02-client/types/client.pb.go @@ -9,6 +9,7 @@ import ( types1 "github.com/Finschia/finschia-sdk/x/upgrade/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" io "io" math "math" math_bits "math/bits" @@ -397,52 +398,53 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/client.proto", fileDescriptor_b6bc4c8185546947) } var fileDescriptor_b6bc4c8185546947 = []byte{ - // 705 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x3f, 0x6f, 0xd3, 0x4e, - 0x18, 0x8e, 0xdb, 0xfc, 0xa2, 0xe6, 0x52, 0x35, 0xfd, 0xb9, 0x29, 0x0d, 0xa1, 0xca, 0x45, 0x27, - 0x86, 0x0c, 0xd4, 0x26, 0x01, 0xa1, 0x2a, 0x1b, 0xc9, 0xd2, 0x0e, 0xa0, 0x60, 0x54, 0x21, 0x58, - 0x22, 0xff, 0xb9, 0x3a, 0x57, 0x39, 0xbe, 0xc8, 0x77, 0x0e, 0xe4, 0x1b, 0x30, 0x32, 0x32, 0x30, - 0xf4, 0x1b, 0xf0, 0x25, 0x18, 0x3a, 0x76, 0x64, 0xb2, 0x50, 0xbb, 0xb0, 0x92, 0x95, 0x05, 0xe5, - 0xee, 0xdc, 0xc6, 0xfd, 0x83, 0x10, 0x6c, 0x77, 0xcf, 0x3d, 0xf7, 0xdc, 0xf3, 0xbc, 0xf6, 0xfb, - 0x02, 0x48, 0x1c, 0xd7, 0x74, 0x69, 0x84, 0x4d, 0x37, 0x20, 0x38, 0xe4, 0xe6, 0xa4, 0xa5, 0x56, - 0xc6, 0x38, 0xa2, 0x9c, 0xea, 0x3a, 0x71, 0x5c, 0x63, 0x4e, 0x30, 0x14, 0x3c, 0x69, 0xd5, 0x2a, - 0x3e, 0xf5, 0xa9, 0x38, 0x36, 0xe7, 0x2b, 0xc9, 0xac, 0xdd, 0xf5, 0x29, 0xf5, 0x03, 0x6c, 0x8a, - 0x9d, 0x13, 0x1f, 0x9a, 0x76, 0x38, 0x55, 0x47, 0xf7, 0x5d, 0xca, 0x46, 0x94, 0x99, 0xf1, 0xd8, - 0x8f, 0x6c, 0x0f, 0x9b, 0x93, 0x96, 0x83, 0xb9, 0xdd, 0x4a, 0xf7, 0x92, 0x85, 0x3e, 0x69, 0x60, - 0x73, 0xdf, 0xc3, 0x21, 0x27, 0x87, 0x04, 0x7b, 0x3d, 0xf1, 0xdc, 0x4b, 0x6e, 0x73, 0xac, 0xb7, - 0x40, 0x51, 0xbe, 0x3e, 0x20, 0x5e, 0x55, 0x6b, 0x68, 0xcd, 0x62, 0xb7, 0x32, 0x4b, 0xe0, 0xfa, - 0xd4, 0x1e, 0x05, 0x1d, 0x74, 0x71, 0x84, 0xac, 0x15, 0xb9, 0xde, 0xf7, 0xf4, 0x3e, 0x58, 0x55, - 0x38, 0x9b, 0x4b, 0x54, 0x97, 0x1a, 0x5a, 0xb3, 0xd4, 0xae, 0x18, 0xd2, 0xa4, 0x91, 0x9a, 0x34, - 0x9e, 0x86, 0xd3, 0xee, 0xd6, 0x2c, 0x81, 0x1b, 0x19, 0x2d, 0x71, 0x07, 0x59, 0x25, 0xf7, 0xd2, - 0x04, 0xfa, 0xac, 0x81, 0x6a, 0x8f, 0x86, 0x0c, 0x87, 0x2c, 0x66, 0x02, 0x7a, 0x45, 0xf8, 0x70, - 0x0f, 0x13, 0x7f, 0xc8, 0xf5, 0x5d, 0x50, 0x18, 0x8a, 0x95, 0xb0, 0x57, 0x6a, 0xd7, 0x8c, 0xeb, - 0x75, 0x33, 0x24, 0xb7, 0x9b, 0x3f, 0x49, 0x60, 0xce, 0x52, 0x7c, 0xfd, 0x35, 0x28, 0xbb, 0xa9, - 0xea, 0x1f, 0x78, 0xad, 0xcd, 0x12, 0x78, 0x47, 0x79, 0xcd, 0x5e, 0x43, 0xd6, 0x9a, 0x9b, 0xb1, - 0x87, 0xbe, 0x68, 0x60, 0x53, 0x96, 0x31, 0xeb, 0x9b, 0xfd, 0x4d, 0x41, 0xdf, 0x81, 0xf5, 0x2b, - 0x0f, 0xb2, 0xea, 0x52, 0x63, 0xb9, 0x59, 0x6a, 0x3f, 0xb8, 0x29, 0xeb, 0x6d, 0x95, 0xea, 0xc2, - 0x79, 0xfa, 0x59, 0x02, 0xb7, 0x6e, 0x0c, 0xc1, 0x90, 0x55, 0xce, 0xa6, 0x60, 0xe8, 0x87, 0x06, - 0x2a, 0x32, 0xc6, 0xc1, 0xd8, 0xb3, 0x39, 0xee, 0x47, 0x74, 0x4c, 0x99, 0x1d, 0xe8, 0x15, 0xf0, - 0x1f, 0x27, 0x3c, 0xc0, 0x32, 0x81, 0x25, 0x37, 0x7a, 0x03, 0x94, 0x3c, 0xcc, 0xdc, 0x88, 0x8c, - 0x39, 0xa1, 0xa1, 0x28, 0x66, 0xd1, 0x5a, 0x84, 0xf4, 0x3d, 0xf0, 0x3f, 0x8b, 0x9d, 0x23, 0xec, - 0xf2, 0xc1, 0x65, 0x15, 0x96, 0x45, 0x15, 0xb6, 0x67, 0x09, 0xac, 0x4a, 0x67, 0xd7, 0x28, 0xc8, - 0x2a, 0x2b, 0xac, 0x97, 0x16, 0xe5, 0x05, 0xa8, 0xb0, 0xd8, 0x61, 0x9c, 0xf0, 0x98, 0xe3, 0x05, - 0xb1, 0xbc, 0x10, 0x83, 0xb3, 0x04, 0xde, 0xbb, 0x10, 0xbb, 0xc6, 0x42, 0x96, 0x7e, 0x09, 0xa7, - 0x92, 0x9d, 0xfc, 0xfb, 0x63, 0x98, 0x43, 0x3f, 0x35, 0x50, 0x3e, 0x90, 0xdd, 0xf1, 0xcf, 0x71, - 0x9f, 0x80, 0xfc, 0x38, 0xb0, 0x43, 0x91, 0xb0, 0xd4, 0xde, 0x36, 0x64, 0x33, 0x1a, 0x69, 0xf3, - 0xa9, 0x66, 0x34, 0xfa, 0x81, 0x1d, 0xaa, 0x7f, 0x53, 0xf0, 0xf5, 0x23, 0xb0, 0xa9, 0x38, 0xde, - 0x20, 0xd3, 0x4b, 0xf9, 0xdf, 0xfc, 0x9f, 0x8d, 0x59, 0x02, 0xb7, 0x65, 0xe6, 0x1b, 0x2f, 0x23, - 0x6b, 0x23, 0xc5, 0x17, 0x3a, 0xbc, 0xb3, 0x3a, 0x4f, 0xfd, 0xf1, 0x18, 0xe6, 0xbe, 0x1f, 0x43, - 0x6d, 0x3e, 0x09, 0x0a, 0xaa, 0xb1, 0x7a, 0xa0, 0x1c, 0xe1, 0x09, 0x61, 0x84, 0x86, 0x83, 0x30, - 0x1e, 0x39, 0x38, 0x12, 0xf1, 0xf3, 0x8b, 0x8d, 0x70, 0x85, 0x80, 0xac, 0xb5, 0x14, 0x79, 0x2e, - 0x80, 0x8c, 0x88, 0x6a, 0xd3, 0xa5, 0x5b, 0x45, 0x24, 0x61, 0x41, 0x44, 0x3a, 0xe9, 0xac, 0xa4, - 0x16, 0xd1, 0x33, 0x50, 0xe8, 0xdb, 0x91, 0x3d, 0x62, 0x73, 0x61, 0x3b, 0x08, 0xe8, 0xdb, 0x8b, - 0x90, 0xac, 0xaa, 0x35, 0x96, 0x9b, 0xc5, 0x45, 0xe1, 0x2b, 0x04, 0x64, 0xad, 0x29, 0x44, 0xe6, - 0x67, 0x5d, 0xeb, 0xe4, 0xac, 0xae, 0x9d, 0x9e, 0xd5, 0xb5, 0x6f, 0x67, 0x75, 0xed, 0xc3, 0x79, - 0x3d, 0x77, 0x7a, 0x5e, 0xcf, 0x7d, 0x3d, 0xaf, 0xe7, 0xde, 0xec, 0xfa, 0x84, 0x0f, 0x63, 0xc7, - 0x70, 0xe9, 0xc8, 0x54, 0x23, 0x94, 0x38, 0xee, 0x8e, 0x4f, 0xcd, 0xc9, 0x63, 0x73, 0x44, 0xbd, - 0x38, 0xc0, 0x4c, 0x4e, 0xef, 0x87, 0xed, 0x1d, 0x35, 0xc0, 0xf9, 0x74, 0x8c, 0x99, 0x53, 0x10, - 0x1f, 0xe5, 0xd1, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x92, 0x4e, 0x61, 0xe0, 0x05, 0x00, - 0x00, + // 734 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbd, 0x6e, 0x13, 0x4b, + 0x18, 0xf5, 0x3a, 0xbe, 0x56, 0x3c, 0xbe, 0x8a, 0x73, 0x37, 0xce, 0x8d, 0xaf, 0x6f, 0xe4, 0xb1, + 0x46, 0x14, 0x16, 0x22, 0xbb, 0xd8, 0x20, 0x14, 0xb9, 0xc3, 0x6e, 0x92, 0x02, 0x64, 0x16, 0x45, + 0x08, 0x1a, 0x6b, 0x7f, 0x26, 0xeb, 0x89, 0xd6, 0x3b, 0xd6, 0xce, 0xac, 0xc1, 0x6f, 0x40, 0x07, + 0x25, 0x48, 0x29, 0xf2, 0x06, 0x34, 0x3c, 0x02, 0x45, 0xca, 0x88, 0x8a, 0x6a, 0x85, 0x92, 0x86, + 0xda, 0x4f, 0x80, 0x3c, 0x33, 0x9b, 0xd8, 0xf9, 0x01, 0x04, 0xdd, 0xcc, 0x99, 0xb3, 0x67, 0xce, + 0x77, 0xbc, 0xc7, 0x0b, 0x20, 0x71, 0x5c, 0xd3, 0xa5, 0x11, 0x36, 0xdd, 0x80, 0xe0, 0x90, 0x9b, + 0xe3, 0xa6, 0x5a, 0x19, 0xa3, 0x88, 0x72, 0xaa, 0xeb, 0xc4, 0x71, 0x8d, 0x19, 0xc1, 0x50, 0xf0, + 0xb8, 0x59, 0x2d, 0xfb, 0xd4, 0xa7, 0xe2, 0xd8, 0x9c, 0xad, 0x24, 0xb3, 0xfa, 0x9f, 0x4f, 0xa9, + 0x1f, 0x60, 0x53, 0xec, 0x9c, 0x78, 0xdf, 0xb4, 0xc3, 0x89, 0x3a, 0xba, 0xe5, 0x52, 0x36, 0xa4, + 0xcc, 0x8c, 0x47, 0x7e, 0x64, 0x7b, 0xd8, 0x1c, 0x37, 0x1d, 0xcc, 0xed, 0x66, 0xba, 0x4f, 0x05, + 0x24, 0xab, 0x2f, 0x95, 0xe5, 0x46, 0x1e, 0xa1, 0x43, 0x0d, 0xac, 0xef, 0x7a, 0x38, 0xe4, 0x64, + 0x9f, 0x60, 0xaf, 0x2b, 0x9c, 0x3c, 0xe5, 0x36, 0xc7, 0x7a, 0x13, 0x14, 0xa4, 0xb1, 0x3e, 0xf1, + 0x2a, 0x5a, 0x5d, 0x6b, 0x14, 0x3a, 0xe5, 0x69, 0x02, 0x57, 0x27, 0xf6, 0x30, 0x68, 0xa3, 0xf3, + 0x23, 0x64, 0x2d, 0xcb, 0xf5, 0xae, 0xa7, 0xf7, 0xc0, 0xdf, 0x0a, 0x67, 0x33, 0x89, 0x4a, 0xb6, + 0xae, 0x35, 0x8a, 0xad, 0xb2, 0x21, 0xfd, 0x1b, 0xa9, 0x7f, 0xe3, 0x61, 0x38, 0xe9, 0x6c, 0x4c, + 0x13, 0xb8, 0xb6, 0xa0, 0x25, 0x9e, 0x41, 0x56, 0xd1, 0xbd, 0x30, 0x81, 0x3e, 0x68, 0xa0, 0xd2, + 0xa5, 0x21, 0xc3, 0x21, 0x8b, 0x99, 0x80, 0x9e, 0x11, 0x3e, 0xd8, 0xc1, 0xc4, 0x1f, 0x70, 0x7d, + 0x1b, 0xe4, 0x07, 0x62, 0x25, 0xec, 0x15, 0x5b, 0x55, 0xe3, 0x6a, 0xa4, 0x86, 0xe4, 0x76, 0x72, + 0xc7, 0x09, 0xcc, 0x58, 0x8a, 0xaf, 0x3f, 0x07, 0x25, 0x37, 0x55, 0xfd, 0x05, 0xaf, 0xd5, 0x69, + 0x02, 0xff, 0x55, 0x5e, 0x17, 0x1f, 0x43, 0xd6, 0x8a, 0xbb, 0x60, 0x0f, 0x7d, 0xd2, 0xc0, 0xba, + 0x8c, 0x71, 0xd1, 0x37, 0xfb, 0x9d, 0x40, 0x5f, 0x81, 0xd5, 0x4b, 0x17, 0xb2, 0x4a, 0xb6, 0xbe, + 0xd4, 0x28, 0xb6, 0xee, 0x5c, 0x37, 0xeb, 0x4d, 0x49, 0x75, 0xe0, 0x6c, 0xfa, 0x69, 0x02, 0x37, + 0xae, 0x1d, 0x82, 0x21, 0xab, 0xb4, 0x38, 0x05, 0x43, 0x6f, 0xb2, 0xa0, 0x2c, 0xc7, 0xd8, 0x1b, + 0x79, 0x36, 0xc7, 0xbd, 0x88, 0x8e, 0x28, 0xb3, 0x03, 0xbd, 0x0c, 0xfe, 0xe2, 0x84, 0x07, 0x58, + 0x4e, 0x60, 0xc9, 0x8d, 0x5e, 0x07, 0x45, 0x0f, 0x33, 0x37, 0x22, 0x23, 0x4e, 0x68, 0x28, 0xc2, + 0x2c, 0x58, 0xf3, 0x90, 0xbe, 0x03, 0xfe, 0x61, 0xb1, 0x73, 0x80, 0x5d, 0xde, 0xbf, 0x48, 0x61, + 0x49, 0xa4, 0xb0, 0x39, 0x4d, 0x60, 0x45, 0x3a, 0xbb, 0x42, 0x41, 0x56, 0x49, 0x61, 0xdd, 0x34, + 0x94, 0x27, 0xa0, 0xcc, 0x62, 0x87, 0x71, 0xc2, 0x63, 0x8e, 0xe7, 0xc4, 0x72, 0x42, 0x0c, 0x4e, + 0x13, 0xf8, 0xff, 0xb9, 0xd8, 0x15, 0x16, 0xb2, 0xf4, 0x0b, 0x38, 0x95, 0x6c, 0xa3, 0xd7, 0x47, + 0x30, 0xf3, 0xf9, 0xe3, 0x56, 0x55, 0x75, 0xc3, 0xa7, 0x63, 0x43, 0x55, 0x69, 0x16, 0x2a, 0xc7, + 0x21, 0x47, 0xef, 0xb3, 0xa0, 0xb4, 0x27, 0x6b, 0xf5, 0xc7, 0x61, 0x3c, 0x00, 0xb9, 0x51, 0x60, + 0x87, 0x62, 0xfe, 0x62, 0x6b, 0xd3, 0x50, 0xd7, 0xa6, 0xad, 0x4d, 0xaf, 0xee, 0x05, 0x76, 0xa8, + 0xde, 0x5c, 0xc1, 0xd7, 0x0f, 0xc0, 0xba, 0xe2, 0x78, 0xfd, 0x85, 0xa6, 0xe5, 0x7e, 0xf0, 0xf6, + 0xd6, 0xa7, 0x09, 0xdc, 0x94, 0x89, 0x5c, 0xfb, 0x30, 0xb2, 0xd6, 0x52, 0x7c, 0xae, 0xff, 0xed, + 0xdb, 0xb3, 0x4c, 0xde, 0x1d, 0xc1, 0xcc, 0xb7, 0x23, 0xa8, 0xfd, 0x24, 0x9b, 0x43, 0x0d, 0xe4, + 0x55, 0x29, 0xbb, 0xa0, 0x14, 0xe1, 0x31, 0x61, 0x84, 0x86, 0xfd, 0x30, 0x1e, 0x3a, 0x38, 0x12, + 0xe1, 0xe4, 0xe6, 0x4b, 0x74, 0x89, 0x80, 0xac, 0x95, 0x14, 0x79, 0x2c, 0x80, 0x05, 0x11, 0x55, + 0xf1, 0xec, 0x8d, 0x22, 0x92, 0x30, 0x27, 0x22, 0x9d, 0xb4, 0x97, 0xd3, 0x01, 0xd0, 0x23, 0x90, + 0xef, 0xd9, 0x91, 0x3d, 0x64, 0x33, 0x61, 0x3b, 0x08, 0xe8, 0xcb, 0xf3, 0x08, 0x58, 0x45, 0xab, + 0x2f, 0x35, 0x0a, 0xf3, 0xc2, 0x97, 0x08, 0xc8, 0x5a, 0x51, 0x88, 0x4c, 0x87, 0x75, 0xac, 0xe3, + 0xd3, 0x9a, 0x76, 0x72, 0x5a, 0xd3, 0xbe, 0x9e, 0xd6, 0xb4, 0xb7, 0x67, 0xb5, 0xcc, 0xc9, 0x59, + 0x2d, 0xf3, 0xe5, 0xac, 0x96, 0x79, 0xb1, 0xed, 0x13, 0x3e, 0x88, 0x1d, 0xc3, 0xa5, 0x43, 0xf5, + 0x37, 0x6b, 0x12, 0xc7, 0xdd, 0xf2, 0xa9, 0x39, 0xbe, 0x6f, 0x0e, 0xa9, 0x17, 0x07, 0x98, 0xc9, + 0x8f, 0xc2, 0xdd, 0xd6, 0x96, 0xfa, 0x2e, 0xf0, 0xc9, 0x08, 0x33, 0x27, 0x2f, 0x7e, 0xb2, 0x7b, + 0xdf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x24, 0x2c, 0x1c, 0x7b, 0x37, 0x06, 0x00, 0x00, } func (this *UpgradeProposal) Equal(that interface{}) bool { diff --git a/modules/core/03-connection/keeper/handshake.go b/modules/core/03-connection/keeper/handshake.go index 713693f8089..8fedc0b995a 100644 --- a/modules/core/03-connection/keeper/handshake.go +++ b/modules/core/03-connection/keeper/handshake.go @@ -1,12 +1,9 @@ package keeper import ( - "bytes" - "github.com/Finschia/finschia-sdk/telemetry" sdk "github.com/Finschia/finschia-sdk/types" sdkerrors "github.com/Finschia/finschia-sdk/types/errors" - "github.com/gogo/protobuf/proto" clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" @@ -28,7 +25,7 @@ func (k Keeper) ConnOpenInit( ) (string, error) { versions := types.GetCompatibleVersions() if version != nil { - if !types.IsSupportedVersion(version) { + if !types.IsSupportedVersion(types.GetCompatibleVersions(), version) { return "", sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported") } @@ -63,7 +60,6 @@ func (k Keeper) ConnOpenInit( // - Identifiers are checked on msg validation func (k Keeper) ConnOpenTry( ctx sdk.Context, - previousConnectionID string, // previousIdentifier counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier delayPeriod uint64, clientID string, // clientID of chainA @@ -75,44 +71,9 @@ func (k Keeper) ConnOpenTry( proofHeight exported.Height, // height at which relayer constructs proof of A storing connectionEnd in state consensusHeight exported.Height, // latest height of chain B which chain A has stored in its chain B client ) (string, error) { - var ( - connectionID string - previousConnection types.ConnectionEnd - found bool - ) - - // empty connection identifier indicates continuing a previous connection handshake - if previousConnectionID != "" { - // ensure that the previous connection exists - previousConnection, found = k.GetConnection(ctx, previousConnectionID) - if !found { - return "", sdkerrors.Wrapf(types.ErrConnectionNotFound, "previous connection does not exist for supplied previous connectionID %s", previousConnectionID) - } - - // ensure that the existing connection's - // counterparty is chainA and connection is on INIT stage. - // Check that existing connection versions for initialized connection is equal to compatible - // versions for this chain. - // ensure that existing connection's delay period is the same as desired delay period. - if !(previousConnection.Counterparty.ConnectionId == "" && - bytes.Equal(previousConnection.Counterparty.Prefix.Bytes(), counterparty.Prefix.Bytes()) && - previousConnection.ClientId == clientID && - previousConnection.Counterparty.ClientId == counterparty.ClientId && - previousConnection.DelayPeriod == delayPeriod) { - return "", sdkerrors.Wrap(types.ErrInvalidConnection, "connection fields mismatch previous connection fields") - } - if !(previousConnection.State == types.INIT) { - return "", sdkerrors.Wrapf(types.ErrInvalidConnectionState, "previous connection state is in state %s, expected INIT", previousConnection.State) - } - - // continue with previous connection - connectionID = previousConnectionID - - } else { - // generate a new connection - connectionID = k.GenerateConnectionIdentifier(ctx) - } + // generate a new connection + connectionID := k.GenerateConnectionIdentifier(ctx) selfHeight := clienttypes.GetSelfHeight(ctx) if consensusHeight.GTE(selfHeight) { @@ -139,15 +100,10 @@ func (k Keeper) ConnOpenTry( expectedCounterparty := types.NewCounterparty(clientID, "", commitmenttypes.NewMerklePrefix(prefix.Bytes())) expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientId, expectedCounterparty, types.ExportedVersionsToProto(counterpartyVersions), delayPeriod) - supportedVersions := types.GetCompatibleVersions() - if len(previousConnection.Versions) != 0 { - supportedVersions = previousConnection.GetVersions() - } - // chain B picks a version from Chain A's available versions that is compatible // with Chain B's supported IBC versions. PickVersion will select the intersection // of the supported versions and the counterparty versions. - version, err := types.PickVersion(supportedVersions, counterpartyVersions) + version, err := types.PickVersion(types.GetCompatibleVersions(), counterpartyVersions) if err != nil { return "", err } @@ -181,7 +137,7 @@ func (k Keeper) ConnOpenTry( } k.SetConnection(ctx, connectionID, connection) - k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", previousConnection.State.String(), "new-state", "TRYOPEN") + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "TRYOPEN") defer func() { telemetry.IncrCounter(1, "ibc", "connection", "open-try") @@ -223,28 +179,19 @@ func (k Keeper) ConnOpenAck( return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) } - // Verify the provided version against the previously set connection state - switch { - // connection on ChainA must be in INIT or TRYOPEN - case connection.State != types.INIT && connection.State != types.TRYOPEN: - return sdkerrors.Wrapf( - types.ErrInvalidConnectionState, - "connection state is not INIT or TRYOPEN (got %s)", connection.State.String(), - ) - - // if the connection is INIT then the provided version must be supproted - case connection.State == types.INIT && !types.IsSupportedVersion(version): + // verify the previously set connection state + if connection.State != types.INIT { return sdkerrors.Wrapf( types.ErrInvalidConnectionState, - "connection state is in INIT but the provided version is not supported %s", version, + "connection state is not INIT (got %s)", connection.State.String(), ) + } - // if the connection is in TRYOPEN then the version must be the only set version in the - // retreived connection state. - case connection.State == types.TRYOPEN && (len(connection.Versions) != 1 || !proto.Equal(connection.Versions[0], version)): + // ensure selected version is supported + if !types.IsSupportedVersion(types.ProtoVersionsToExported(connection.Versions), version) { return sdkerrors.Wrapf( types.ErrInvalidConnectionState, - "connection state is in TRYOPEN but the provided version (%s) is not set in the previous connection versions %s", version, connection.Versions, + "the counterparty selected version %s is not supported by versions selected on INIT", version, ) } @@ -283,7 +230,7 @@ func (k Keeper) ConnOpenAck( return err } - k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", connection.State.String(), "new-state", "OPEN") + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "INIT", "new-state", "OPEN") defer func() { telemetry.IncrCounter(1, "ibc", "connection", "open-ack") diff --git a/modules/core/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index 5f322e4e213..aeabf5718c2 100644 --- a/modules/core/03-connection/keeper/handshake_test.go +++ b/modules/core/03-connection/keeper/handshake_test.go @@ -80,12 +80,11 @@ func (suite *KeeperTestSuite) TestConnOpenInit() { // connection on chainA is INIT func (suite *KeeperTestSuite) TestConnOpenTry() { var ( - path *ibctesting.Path - delayPeriod uint64 - previousConnectionID string - versions []exported.Version - consensusHeight exported.Height - counterpartyClient exported.ClientState + path *ibctesting.Path + delayPeriod uint64 + versions []exported.Version + consensusHeight exported.Height + counterpartyClient exported.ClientState ) testCases := []struct { @@ -100,15 +99,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { // retrieve client state of chainA to pass as counterpartyClient counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) }, true}, - {"success with crossing hellos", func() { - err := suite.coordinator.ConnOpenInitOnBothChains(path) - suite.Require().NoError(err) - - // retrieve client state of chainA to pass as counterpartyClient - counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) - - previousConnectionID = path.EndpointB.ConnectionID - }, true}, {"success with delay period", func() { err := path.EndpointA.ConnOpenInit() suite.Require().NoError(err) @@ -213,49 +203,6 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { err := path.EndpointA.ConnOpenInit() suite.Require().NoError(err) }, false}, - {"invalid previous connection is in TRYOPEN", func() { - // open init chainA - err := path.EndpointA.ConnOpenInit() - suite.Require().NoError(err) - - // open try chainB - err = path.EndpointB.ConnOpenTry() - suite.Require().NoError(err) - - err = path.EndpointB.UpdateClient() - suite.Require().NoError(err) - - // retrieve client state of chainA to pass as counterpartyClient - counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) - - previousConnectionID = path.EndpointB.ConnectionID - }, false}, - {"invalid previous connection has invalid versions", func() { - // open init chainA - err := path.EndpointA.ConnOpenInit() - suite.Require().NoError(err) - - // open try chainB - err = path.EndpointB.ConnOpenTry() - suite.Require().NoError(err) - - // modify connB to be in INIT with incorrect versions - connection, found := suite.chainB.App.GetIBCKeeper().ConnectionKeeper.GetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID) - suite.Require().True(found) - - connection.State = types.INIT - connection.Versions = []*types.Version{{}} - - suite.chainB.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID, connection) - - err = path.EndpointB.UpdateClient() - suite.Require().NoError(err) - - // retrieve client state of chainA to pass as counterpartyClient - counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) - - previousConnectionID = path.EndpointB.ConnectionID - }, false}, } for _, tc := range testCases { @@ -263,9 +210,9 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { suite.Run(tc.msg, func() { suite.SetupTest() // reset - consensusHeight = clienttypes.ZeroHeight() // must be explicitly changed in malleate - versions = types.GetCompatibleVersions() // must be explicitly changed in malleate - previousConnectionID = "" + consensusHeight = clienttypes.ZeroHeight() // may be changed in malleate + versions = types.GetCompatibleVersions() // may be changed in malleate + delayPeriod = 0 // may be changed in malleate path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) @@ -292,7 +239,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { proofClient, _ := suite.chainA.QueryProof(clientKey) connectionID, err := suite.chainB.App.GetIBCKeeper().ConnectionKeeper.ConnOpenTry( - suite.chainB.GetContext(), previousConnectionID, counterparty, delayPeriod, path.EndpointB.ClientID, counterpartyClient, + suite.chainB.GetContext(), counterparty, delayPeriod, path.EndpointB.ClientID, counterpartyClient, versions, proofInit, proofClient, proofConsensus, proofHeight, consensusHeight, ) @@ -333,27 +280,6 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { // retrieve client state of chainB to pass as counterpartyClient counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) }, true}, - {"success from tryopen", func() { - // chainA is in TRYOPEN, chainB is in TRYOPEN - err := path.EndpointB.ConnOpenInit() - suite.Require().NoError(err) - - err = path.EndpointA.ConnOpenTry() - suite.Require().NoError(err) - - // set chainB to TRYOPEN - connection := path.EndpointB.GetConnection() - connection.State = types.TRYOPEN - connection.Counterparty.ConnectionId = path.EndpointA.ConnectionID - suite.chainB.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID, connection) - // update path.EndpointB.ClientID so state change is committed - path.EndpointB.UpdateClient() - - path.EndpointA.UpdateClient() - - // retrieve client state of chainB to pass as counterpartyClient - counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) - }, true}, {"invalid counterparty client", func() { err := path.EndpointA.ConnOpenInit() suite.Require().NoError(err) @@ -440,28 +366,6 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { version = types.NewVersion("2.0", nil) }, false}, - {"connection is in TRYOPEN but the set version in the connection is invalid", func() { - // chainA is in TRYOPEN, chainB is in TRYOPEN - err := path.EndpointB.ConnOpenInit() - suite.Require().NoError(err) - - err = path.EndpointA.ConnOpenTry() - suite.Require().NoError(err) - - // set chainB to TRYOPEN - connection := path.EndpointB.GetConnection() - connection.State = types.TRYOPEN - suite.chainB.App.GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), path.EndpointB.ConnectionID, connection) - - // update path.EndpointB.ClientID so state change is committed - path.EndpointB.UpdateClient() - path.EndpointA.UpdateClient() - - // retrieve client state of chainB to pass as counterpartyClient - counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) - - version = types.NewVersion("2.0", nil) - }, false}, {"incompatible IBC versions", func() { err := path.EndpointA.ConnOpenInit() suite.Require().NoError(err) diff --git a/modules/core/03-connection/types/msgs.go b/modules/core/03-connection/types/msgs.go index f27064bbd70..be5530fa187 100644 --- a/modules/core/03-connection/types/msgs.go +++ b/modules/core/03-connection/types/msgs.go @@ -76,8 +76,8 @@ func (msg MsgConnectionOpenInit) GetSigners() []sdk.AccAddress { // //nolint:interfacer func NewMsgConnectionOpenTry( - previousConnectionID, clientID, counterpartyConnectionID, - counterpartyClientID string, counterpartyClient exported.ClientState, + clientID, counterpartyConnectionID, counterpartyClientID string, + counterpartyClient exported.ClientState, counterpartyPrefix commitmenttypes.MerklePrefix, counterpartyVersions []*Version, delayPeriod uint64, proofInit, proofClient, proofConsensus []byte, @@ -86,7 +86,6 @@ func NewMsgConnectionOpenTry( counterparty := NewCounterparty(counterpartyClientID, counterpartyConnectionID, counterpartyPrefix) csAny, _ := clienttypes.PackClientState(counterpartyClient) return &MsgConnectionOpenTry{ - PreviousConnectionId: previousConnectionID, ClientId: clientID, ClientState: csAny, Counterparty: counterparty, @@ -103,11 +102,8 @@ func NewMsgConnectionOpenTry( // ValidateBasic implements sdk.Msg func (msg MsgConnectionOpenTry) ValidateBasic() error { - // an empty connection identifier indicates that a connection identifier should be generated if msg.PreviousConnectionId != "" { - if !IsValidConnectionID(msg.PreviousConnectionId) { - return sdkerrors.Wrap(ErrInvalidConnectionIdentifier, "invalid previous connection ID") - } + return sdkerrors.Wrap(ErrInvalidConnectionIdentifier, "previous connection identifier must be empty, this field has been deprecated as crossing hellos are no longer supported") } if err := host.ClientIdentifierValidator(msg.ClientId); err != nil { return sdkerrors.Wrap(err, "invalid client ID") diff --git a/modules/core/03-connection/types/msgs_test.go b/modules/core/03-connection/types/msgs_test.go index ed7a7a54c5d..598f2442878 100644 --- a/modules/core/03-connection/types/msgs_test.go +++ b/modules/core/03-connection/types/msgs_test.go @@ -47,8 +47,7 @@ func (suite *MsgTestSuite) SetupTest() { app := simapp.Setup(false) db := dbm.NewMemDB() - dblog := log.NewNopLogger() - store := rootmulti.NewStore(db, dblog) + store := rootmulti.NewStore(db, log.NewNopLogger()) storeKey := storetypes.NewKVStoreKey("iavlStoreKey") store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) @@ -112,6 +111,8 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { clientState := ibctmtypes.NewClientState( chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, ) + any, err := clienttypes.PackClientState(clientState) + suite.Require().NoError(err) // Pack consensus state into any to test unpacking error consState := ibctmtypes.NewConsensusState( @@ -130,24 +131,23 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { msg *types.MsgConnectionOpenTry expPass bool }{ - {"invalid connection ID", types.NewMsgConnectionOpenTry("test/conn1", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid connection ID", types.NewMsgConnectionOpenTry("(invalidconnection)", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid client ID", types.NewMsgConnectionOpenTry(connectionID, "test/iris", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid counterparty connection ID", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "ibc/test", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid counterparty client ID", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "test/conn1", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid nil counterparty client", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", nil, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"invalid client unpacking", &types.MsgConnectionOpenTry{connectionID, "clienttotesta", invalidAny, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer}, false}, - {"counterparty failed validate", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"empty counterparty prefix", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, emptyPrefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"empty counterpartyVersions", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"empty proofInit", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, - {"empty proofClient", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, signer), false}, - {"empty proofConsensus", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, signer), false}, - {"invalid proofHeight", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clienttypes.ZeroHeight(), clientHeight, signer), false}, - {"invalid consensusHeight", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clienttypes.ZeroHeight(), signer), false}, - {"empty singer", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ""), false}, - {"success", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), true}, - {"invalid version", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{{}}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"non empty connection ID", &types.MsgConnectionOpenTry{"connection-0", "clienttotesta", any, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer}, false}, + {"invalid client ID", types.NewMsgConnectionOpenTry("test/iris", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid counterparty connection ID", types.NewMsgConnectionOpenTry("clienttotesta", "ibc/test", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid counterparty client ID", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "test/conn1", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid nil counterparty client", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", nil, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid client unpacking", &types.MsgConnectionOpenTry{"", "clienttotesta", invalidAny, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer}, false}, + {"counterparty failed validate", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty counterparty prefix", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, emptyPrefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty counterpartyVersions", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty proofInit", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty proofClient", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty proofConsensus", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, signer), false}, + {"invalid proofHeight", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clienttypes.ZeroHeight(), clientHeight, signer), false}, + {"invalid consensusHeight", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clienttypes.ZeroHeight(), signer), false}, + {"empty singer", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ""), false}, + {"success", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), true}, + {"invalid version", types.NewMsgConnectionOpenTry("clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{{}}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, } for _, tc := range testCases { diff --git a/modules/core/03-connection/types/tx.pb.go b/modules/core/03-connection/types/tx.pb.go index 7f7a72151f0..af19409c05b 100644 --- a/modules/core/03-connection/types/tx.pb.go +++ b/modules/core/03-connection/types/tx.pb.go @@ -115,9 +115,8 @@ var xxx_messageInfo_MsgConnectionOpenInitResponse proto.InternalMessageInfo // connection on Chain B. type MsgConnectionOpenTry struct { ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - // in the case of crossing hello's, when both chains call OpenInit, we need - // the connection identifier of the previous connection in state INIT - PreviousConnectionId string `protobuf:"bytes,2,opt,name=previous_connection_id,json=previousConnectionId,proto3" json:"previous_connection_id,omitempty" yaml:"previous_connection_id"` + // Deprecated: this field is unused. Crossing hellos are no longer supported in core IBC. + PreviousConnectionId string `protobuf:"bytes,2,opt,name=previous_connection_id,json=previousConnectionId,proto3" json:"previous_connection_id,omitempty" yaml:"previous_connection_id"` // Deprecated: Do not use. ClientState *types.Any `protobuf:"bytes,3,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` Counterparty Counterparty `protobuf:"bytes,4,opt,name=counterparty,proto3" json:"counterparty"` DelayPeriod uint64 `protobuf:"varint,5,opt,name=delay_period,json=delayPeriod,proto3" json:"delay_period,omitempty" yaml:"delay_period"` @@ -388,65 +387,66 @@ func init() { func init() { proto.RegisterFile("ibc/core/connection/v1/tx.proto", fileDescriptor_5d00fde5fc97399e) } var fileDescriptor_5d00fde5fc97399e = []byte{ - // 927 bytes of a gzipped FileDescriptorProto + // 929 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x31, 0x73, 0xe3, 0x44, - 0x14, 0xb6, 0x62, 0x27, 0xb1, 0xd7, 0x86, 0xbb, 0x5b, 0x9c, 0x44, 0x98, 0x3b, 0xcb, 0xa7, 0x81, - 0x21, 0x05, 0x91, 0xce, 0x77, 0x61, 0x06, 0x32, 0x50, 0xc4, 0x6e, 0x48, 0x71, 0x70, 0x23, 0x6e, - 0x8e, 0x99, 0x6b, 0x3c, 0xf6, 0x7a, 0xa3, 0xec, 0xd8, 0xd6, 0x6a, 0xb4, 0xb2, 0x41, 0xb4, 0x34, - 0x0c, 0x15, 0x0d, 0xfd, 0xfd, 0x07, 0xfe, 0xc4, 0x95, 0x57, 0x52, 0x69, 0x20, 0x69, 0xa8, 0xd5, - 0xd1, 0x31, 0xda, 0x95, 0xe4, 0xb5, 0x23, 0x0f, 0x31, 0xce, 0x75, 0xfb, 0xf6, 0x7d, 0xef, 0xbd, - 0xdd, 0xf7, 0xbe, 0x6f, 0x67, 0x81, 0x46, 0x06, 0xc8, 0x44, 0xd4, 0xc3, 0x26, 0xa2, 0x8e, 0x83, - 0x91, 0x4f, 0xa8, 0x63, 0xce, 0xda, 0xa6, 0xff, 0x83, 0xe1, 0x7a, 0xd4, 0xa7, 0x70, 0x9f, 0x0c, - 0x90, 0x11, 0x03, 0x8c, 0x39, 0xc0, 0x98, 0xb5, 0x1b, 0x75, 0x9b, 0xda, 0x94, 0x43, 0xcc, 0x78, - 0x25, 0xd0, 0x8d, 0xf7, 0x6d, 0x4a, 0xed, 0x31, 0x36, 0xb9, 0x35, 0x98, 0x9e, 0x9b, 0x7d, 0x27, - 0x48, 0x5c, 0x52, 0xa5, 0x31, 0xc1, 0x8e, 0x1f, 0x57, 0x11, 0xab, 0x04, 0xf0, 0xf1, 0x8a, 0xa3, - 0x48, 0x75, 0x39, 0x50, 0xff, 0x7d, 0x0b, 0xec, 0x3d, 0x65, 0x76, 0x37, 0xdb, 0xff, 0xc6, 0xc5, - 0xce, 0x99, 0x43, 0x7c, 0xd8, 0x06, 0x15, 0x91, 0xb2, 0x47, 0x86, 0xaa, 0xd2, 0x52, 0x0e, 0x2b, - 0x9d, 0x7a, 0x14, 0x6a, 0x77, 0x83, 0xfe, 0x64, 0x7c, 0xa2, 0x67, 0x2e, 0xdd, 0x2a, 0x8b, 0xf5, - 0xd9, 0x10, 0x7e, 0x0d, 0x6a, 0x88, 0x4e, 0x1d, 0x1f, 0x7b, 0x6e, 0xdf, 0xf3, 0x03, 0x75, 0xab, - 0xa5, 0x1c, 0x56, 0x1f, 0x7f, 0x68, 0xe4, 0x5f, 0xdb, 0xe8, 0x4a, 0xd8, 0x4e, 0xe9, 0x75, 0xa8, - 0x15, 0xac, 0x85, 0x78, 0xf8, 0x39, 0xd8, 0x9d, 0x61, 0x8f, 0x11, 0xea, 0xa8, 0x45, 0x9e, 0x4a, - 0x5b, 0x95, 0xea, 0x85, 0x80, 0x59, 0x29, 0x1e, 0x9e, 0x80, 0xda, 0x10, 0x8f, 0xfb, 0x41, 0xcf, - 0xc5, 0x1e, 0xa1, 0x43, 0xb5, 0xd4, 0x52, 0x0e, 0x4b, 0x9d, 0x83, 0x28, 0xd4, 0xde, 0x13, 0x17, - 0x90, 0xbd, 0xba, 0x55, 0xe5, 0xe6, 0x33, 0x6e, 0xc1, 0x7d, 0xb0, 0xc3, 0x88, 0xed, 0x60, 0x4f, - 0xdd, 0x8e, 0xaf, 0x6d, 0x25, 0xd6, 0x49, 0xf9, 0xe7, 0x57, 0x5a, 0xe1, 0xef, 0x57, 0x5a, 0x41, - 0xd7, 0xc0, 0x83, 0xdc, 0xa6, 0x59, 0x98, 0xb9, 0xd4, 0x61, 0x58, 0xff, 0x6d, 0x17, 0xd4, 0xaf, - 0x21, 0x9e, 0x7b, 0xc1, 0xff, 0xe9, 0xea, 0x77, 0x60, 0xdf, 0xf5, 0xf0, 0x8c, 0xd0, 0x29, 0xeb, - 0xcd, 0x6f, 0x1d, 0xc7, 0x6f, 0xf1, 0xf8, 0x87, 0x51, 0xa8, 0x3d, 0x10, 0xf1, 0xf9, 0x38, 0xdd, - 0xaa, 0xa7, 0x8e, 0xf9, 0x81, 0xce, 0x86, 0xf0, 0x19, 0xa8, 0x25, 0x05, 0x99, 0xdf, 0xf7, 0x71, - 0xd2, 0xe3, 0xba, 0x21, 0x78, 0x67, 0xa4, 0xbc, 0x33, 0x4e, 0x9d, 0x40, 0xee, 0x9c, 0x1c, 0xa3, - 0x5b, 0x55, 0x61, 0x7e, 0x1b, 0x5b, 0xd7, 0x08, 0x50, 0xda, 0x90, 0x00, 0xcb, 0x53, 0xdc, 0x5e, - 0x63, 0x8a, 0x33, 0xb0, 0x27, 0xe7, 0xea, 0x25, 0xcc, 0x60, 0xea, 0x4e, 0xab, 0x78, 0x03, 0x2a, - 0x75, 0x5a, 0x51, 0xa8, 0xdd, 0x4f, 0x6e, 0x9c, 0x97, 0x47, 0xb7, 0xea, 0xf2, 0x7e, 0x12, 0xc6, - 0xe0, 0x4b, 0x50, 0x73, 0x3d, 0x4a, 0xcf, 0x7b, 0x17, 0x98, 0xd8, 0x17, 0xbe, 0xba, 0xcb, 0x7b, - 0xd0, 0x90, 0xca, 0x09, 0xa1, 0xce, 0xda, 0xc6, 0x57, 0x1c, 0xd1, 0xf9, 0x20, 0xbe, 0xf9, 0xfc, - 0x4e, 0x72, 0xb4, 0x6e, 0x55, 0xb9, 0x29, 0x90, 0xf0, 0x18, 0x00, 0xe1, 0x25, 0x0e, 0xf1, 0xd5, - 0x72, 0x4b, 0x39, 0xac, 0x75, 0xf6, 0xa2, 0x50, 0xbb, 0x27, 0x47, 0xc6, 0x3e, 0xdd, 0xaa, 0x70, - 0x83, 0x2b, 0xf9, 0x24, 0x3d, 0x91, 0xa8, 0xac, 0x56, 0x78, 0xdc, 0xc1, 0x72, 0x45, 0xe1, 0x4d, - 0x2b, 0x76, 0xb9, 0x05, 0xbb, 0xe0, 0x4e, 0xe2, 0x8d, 0x79, 0xed, 0xb0, 0x29, 0x53, 0x01, 0x0f, - 0x6f, 0x44, 0xa1, 0xb6, 0xbf, 0x10, 0x9e, 0x02, 0x74, 0xeb, 0x5d, 0x91, 0x21, 0xdd, 0x80, 0xe7, - 0xe0, 0x6e, 0xe6, 0x4d, 0xdb, 0x52, 0xfd, 0xcf, 0xb6, 0x68, 0x49, 0x5b, 0x0e, 0xd2, 0x21, 0x2c, - 0x66, 0xd0, 0xad, 0x3b, 0xd9, 0x56, 0xd2, 0x9e, 0xb9, 0x70, 0x6b, 0x2b, 0x84, 0xdb, 0x04, 0xf7, - 0xf3, 0x64, 0x99, 0xe9, 0xf6, 0xaf, 0xed, 0x1c, 0xdd, 0x9e, 0xa2, 0x11, 0xfc, 0x12, 0xbc, 0xb3, - 0xa8, 0x3d, 0xa1, 0x5d, 0x35, 0x0a, 0xb5, 0x7a, 0x76, 0x3e, 0x59, 0x72, 0x35, 0x24, 0x4b, 0x0d, - 0x81, 0xc6, 0x02, 0x89, 0xf2, 0x74, 0xfc, 0x51, 0x14, 0x6a, 0x0f, 0x73, 0x08, 0xb7, 0x94, 0x58, - 0x95, 0x9d, 0x0b, 0x7a, 0xde, 0xe0, 0xb9, 0x5c, 0x7e, 0x0a, 0x4a, 0x1b, 0x3f, 0x05, 0xcb, 0x32, - 0xd8, 0xbe, 0x45, 0x19, 0xb4, 0x81, 0x60, 0x77, 0xcf, 0xf7, 0x02, 0x75, 0x87, 0xd3, 0x51, 0x7a, - 0x44, 0x33, 0x97, 0x6e, 0x95, 0xf9, 0x3a, 0x7e, 0x77, 0x97, 0x35, 0xb0, 0xbb, 0x99, 0x06, 0xca, - 0xb7, 0xa2, 0x81, 0xca, 0x5b, 0xd5, 0x00, 0x58, 0x43, 0x03, 0xa7, 0x68, 0x94, 0x69, 0xe0, 0x97, - 0x2d, 0xa0, 0x5e, 0x03, 0x74, 0xa9, 0x73, 0x4e, 0xbc, 0xc9, 0xa6, 0x3a, 0xc8, 0x26, 0xd7, 0x47, - 0x23, 0x4e, 0xfb, 0x9c, 0xc9, 0xf5, 0xd1, 0x28, 0x9d, 0x5c, 0xac, 0xbc, 0x65, 0x22, 0x15, 0x6f, - 0x91, 0x48, 0xf3, 0x66, 0x95, 0x56, 0x34, 0x4b, 0x07, 0xad, 0x55, 0xbd, 0x48, 0x1b, 0xf6, 0xf8, - 0x9f, 0x22, 0x28, 0x3e, 0x65, 0x36, 0xfc, 0x11, 0xc0, 0x9c, 0x7f, 0xd4, 0xd1, 0x2a, 0x11, 0xe6, - 0xfe, 0x20, 0x1a, 0x9f, 0xae, 0x05, 0x4f, 0xcf, 0x00, 0xbf, 0x07, 0xf7, 0xae, 0x7f, 0x36, 0x3e, - 0xb9, 0x71, 0xae, 0xe7, 0x5e, 0xd0, 0x38, 0x5e, 0x07, 0xbd, 0xba, 0x70, 0x3c, 0xb3, 0x9b, 0x17, - 0x3e, 0x45, 0xa3, 0x35, 0x0a, 0x4b, 0x34, 0x85, 0x3f, 0x29, 0x60, 0x2f, 0x9f, 0xa3, 0x8f, 0x6e, - 0x9c, 0x2f, 0x89, 0x68, 0x7c, 0xb6, 0x6e, 0x44, 0x7a, 0x8a, 0xce, 0x8b, 0xd7, 0x97, 0x4d, 0xe5, - 0xcd, 0x65, 0x53, 0xf9, 0xf3, 0xb2, 0xa9, 0xfc, 0x7a, 0xd5, 0x2c, 0xbc, 0xb9, 0x6a, 0x16, 0xfe, - 0xb8, 0x6a, 0x16, 0x5e, 0x7e, 0x61, 0x13, 0xff, 0x62, 0x3a, 0x30, 0x10, 0x9d, 0x98, 0x88, 0xb2, - 0x09, 0x65, 0x26, 0x19, 0xa0, 0x23, 0x9b, 0x9a, 0xb3, 0x63, 0x73, 0x42, 0x87, 0xd3, 0x31, 0x66, - 0xe2, 0x8b, 0xfe, 0xe8, 0xc9, 0x91, 0xf4, 0x4b, 0xf7, 0x03, 0x17, 0xb3, 0xc1, 0x0e, 0x7f, 0x72, - 0x9f, 0xfc, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x63, 0x2e, 0xb2, 0x54, 0x0c, 0x00, 0x00, + 0x14, 0xb6, 0x62, 0x27, 0xb1, 0xd7, 0x86, 0xbb, 0x5b, 0x9c, 0x44, 0x88, 0x3b, 0xcb, 0x08, 0x18, + 0x52, 0x10, 0xe9, 0x7c, 0x17, 0x66, 0x20, 0x03, 0x45, 0xec, 0x86, 0x14, 0x07, 0x37, 0xe2, 0xe6, + 0x66, 0xb8, 0xc6, 0x63, 0xcb, 0x1b, 0x65, 0xc7, 0xb6, 0x56, 0xa3, 0x95, 0x0d, 0xa2, 0xa5, 0x61, + 0xa8, 0xe8, 0x68, 0xef, 0x3f, 0xf0, 0x27, 0xae, 0xbc, 0x92, 0x4a, 0x03, 0x49, 0x43, 0xad, 0x8e, + 0x8e, 0xd1, 0xae, 0x24, 0xaf, 0x6d, 0x79, 0xb0, 0x71, 0xae, 0xdb, 0xb7, 0xef, 0x7b, 0xef, 0xed, + 0xbe, 0xf7, 0x7d, 0x3b, 0x0b, 0x54, 0xdc, 0xb7, 0x0c, 0x8b, 0x78, 0xc8, 0xb0, 0x88, 0xe3, 0x20, + 0xcb, 0xc7, 0xc4, 0x31, 0xa6, 0x2d, 0xc3, 0xff, 0x41, 0x77, 0x3d, 0xe2, 0x13, 0x78, 0x88, 0xfb, + 0x96, 0x1e, 0x03, 0xf4, 0x19, 0x40, 0x9f, 0xb6, 0x94, 0xba, 0x4d, 0x6c, 0xc2, 0x20, 0x46, 0xbc, + 0xe2, 0x68, 0xe5, 0x5d, 0x9b, 0x10, 0x7b, 0x84, 0x0c, 0x66, 0xf5, 0x27, 0x97, 0x46, 0xcf, 0x09, + 0x12, 0x97, 0x50, 0x69, 0x84, 0x91, 0xe3, 0xc7, 0x55, 0xf8, 0x2a, 0x01, 0x7c, 0xbc, 0xe2, 0x28, + 0x42, 0x5d, 0x06, 0xd4, 0x7e, 0xdf, 0x01, 0x07, 0x4f, 0xa8, 0xdd, 0xc9, 0xf6, 0xbf, 0x71, 0x91, + 0x73, 0xe1, 0x60, 0x1f, 0xb6, 0x40, 0x85, 0xa7, 0xec, 0xe2, 0x81, 0x2c, 0x35, 0xa5, 0xe3, 0x4a, + 0xbb, 0x1e, 0x85, 0xea, 0xdd, 0xa0, 0x37, 0x1e, 0x9d, 0x69, 0x99, 0x4b, 0x33, 0xcb, 0x7c, 0x7d, + 0x31, 0x80, 0x5f, 0x83, 0x9a, 0x45, 0x26, 0x8e, 0x8f, 0x3c, 0xb7, 0xe7, 0xf9, 0x81, 0xbc, 0xd3, + 0x94, 0x8e, 0xab, 0x8f, 0x3e, 0xd4, 0xf3, 0xaf, 0xad, 0x77, 0x04, 0x6c, 0xbb, 0xf4, 0x2a, 0x54, + 0x0b, 0xe6, 0x5c, 0x3c, 0xfc, 0x1c, 0xec, 0x4f, 0x91, 0x47, 0x31, 0x71, 0xe4, 0x22, 0x4b, 0xa5, + 0xae, 0x4a, 0xf5, 0x9c, 0xc3, 0xcc, 0x14, 0x0f, 0xcf, 0x40, 0x6d, 0x80, 0x46, 0xbd, 0xa0, 0xeb, + 0x22, 0x0f, 0x93, 0x81, 0x5c, 0x6a, 0x4a, 0xc7, 0xa5, 0xf6, 0x51, 0x14, 0xaa, 0xef, 0xf0, 0x0b, + 0x88, 0x5e, 0xcd, 0xac, 0x32, 0xf3, 0x29, 0xb3, 0xe0, 0x21, 0xd8, 0xa3, 0xd8, 0x76, 0x90, 0x27, + 0xef, 0xc6, 0xd7, 0x36, 0x13, 0xeb, 0xac, 0xfc, 0xf3, 0x4b, 0xb5, 0xf0, 0xf7, 0x4b, 0xb5, 0xa0, + 0xa9, 0xe0, 0x41, 0x6e, 0xd3, 0x4c, 0x44, 0x5d, 0xe2, 0x50, 0xa4, 0xfd, 0xb6, 0x0f, 0xea, 0x4b, + 0x88, 0x67, 0x5e, 0xf0, 0x7f, 0xba, 0xfa, 0x1d, 0x38, 0x74, 0x3d, 0x34, 0xc5, 0x64, 0x42, 0xbb, + 0xb3, 0x5b, 0xc7, 0xf1, 0x3b, 0x2c, 0xfe, 0x83, 0x28, 0x54, 0x1f, 0xf0, 0xf8, 0x7c, 0x9c, 0x26, + 0x4b, 0x66, 0x3d, 0x75, 0xcd, 0x8e, 0x74, 0x31, 0x80, 0x4f, 0x41, 0x2d, 0x29, 0x49, 0xfd, 0x9e, + 0x8f, 0x92, 0x2e, 0xd7, 0x75, 0xce, 0x3c, 0x3d, 0x65, 0x9e, 0x7e, 0xee, 0x04, 0x62, 0xef, 0xc4, + 0x18, 0xcd, 0xac, 0x72, 0xf3, 0xdb, 0xd8, 0x5a, 0xa2, 0x40, 0x69, 0x4b, 0x0a, 0x2c, 0xce, 0x71, + 0x77, 0x83, 0x39, 0x4e, 0xc1, 0x81, 0x98, 0xab, 0x9b, 0x70, 0x83, 0xca, 0x7b, 0xcd, 0xe2, 0x1a, + 0x64, 0x6a, 0x37, 0xa3, 0x50, 0xbd, 0x9f, 0xdc, 0x38, 0x2f, 0x8f, 0x66, 0xd6, 0xc5, 0xfd, 0x24, + 0x8c, 0xc2, 0x17, 0xa0, 0xe6, 0x7a, 0x84, 0x5c, 0x76, 0xaf, 0x10, 0xb6, 0xaf, 0x7c, 0x79, 0x9f, + 0xf5, 0x40, 0x11, 0xca, 0x71, 0xa9, 0x4e, 0x5b, 0xfa, 0x57, 0x0c, 0xd1, 0x7e, 0x2f, 0xbe, 0xf9, + 0xec, 0x4e, 0x62, 0xb4, 0x66, 0x56, 0x99, 0xc9, 0x91, 0xf0, 0x14, 0x00, 0xee, 0xc5, 0x0e, 0xf6, + 0xe5, 0x72, 0x53, 0x3a, 0xae, 0xb5, 0x0f, 0xa2, 0x50, 0xbd, 0x27, 0x46, 0xc6, 0x3e, 0xcd, 0xac, + 0x30, 0x83, 0x69, 0xf9, 0x2c, 0x3d, 0x11, 0xaf, 0x2c, 0x57, 0x58, 0xdc, 0xd1, 0x62, 0x45, 0xee, + 0x4d, 0x2b, 0x76, 0x98, 0x05, 0x3b, 0xe0, 0x4e, 0xe2, 0x8d, 0x99, 0xed, 0xd0, 0x09, 0x95, 0x01, + 0x0b, 0x57, 0xa2, 0x50, 0x3d, 0x9c, 0x0b, 0x4f, 0x01, 0x9a, 0xf9, 0x36, 0xcf, 0x90, 0x6e, 0xc0, + 0x4b, 0x70, 0x37, 0xf3, 0xa6, 0x6d, 0xa9, 0xfe, 0x67, 0x5b, 0xd4, 0xa4, 0x2d, 0x47, 0xe9, 0x10, + 0xe6, 0x33, 0x68, 0xe6, 0x9d, 0x6c, 0x2b, 0x69, 0xcf, 0x4c, 0xba, 0xb5, 0x15, 0xd2, 0x6d, 0x80, + 0xfb, 0x79, 0xc2, 0xcc, 0x94, 0xfb, 0xd7, 0x6e, 0x8e, 0x72, 0xcf, 0xad, 0x21, 0xfc, 0x12, 0xbc, + 0x35, 0xaf, 0x3e, 0xae, 0x5e, 0x39, 0x0a, 0xd5, 0x7a, 0x76, 0x3e, 0x41, 0x74, 0x31, 0x91, 0x05, + 0xa9, 0x59, 0x40, 0x99, 0x23, 0x51, 0x9e, 0x92, 0x3f, 0x8a, 0x42, 0xf5, 0xfd, 0x1c, 0xc2, 0x2d, + 0x24, 0x96, 0x45, 0xe7, 0x9c, 0x9e, 0xb7, 0x78, 0x30, 0x17, 0x9f, 0x82, 0xd2, 0xd6, 0x4f, 0xc1, + 0xa2, 0x0c, 0x76, 0x6f, 0x51, 0x06, 0x2d, 0xc0, 0xd9, 0xdd, 0xf5, 0xbd, 0x40, 0xde, 0x63, 0x74, + 0x14, 0x9e, 0xd1, 0xcc, 0xa5, 0x99, 0x65, 0xb6, 0x8e, 0x5f, 0xde, 0x45, 0x0d, 0xec, 0x6f, 0xa7, + 0x81, 0xf2, 0xad, 0x68, 0xa0, 0xf2, 0x46, 0x35, 0x00, 0x36, 0xd0, 0xc0, 0xb9, 0x35, 0xcc, 0x34, + 0xf0, 0xcb, 0x0e, 0x90, 0x97, 0x00, 0x1d, 0xe2, 0x5c, 0x62, 0x6f, 0xbc, 0xad, 0x0e, 0xb2, 0xc9, + 0xf5, 0xac, 0x21, 0xa3, 0x7d, 0xce, 0xe4, 0x7a, 0xd6, 0x30, 0x9d, 0x5c, 0xac, 0xbc, 0x45, 0x22, + 0x15, 0x6f, 0x91, 0x48, 0xb3, 0x66, 0x95, 0x56, 0x34, 0x4b, 0x03, 0xcd, 0x55, 0xbd, 0x48, 0x1b, + 0xf6, 0xe8, 0x9f, 0x22, 0x28, 0x3e, 0xa1, 0x36, 0xfc, 0x11, 0xc0, 0x9c, 0x9f, 0xd4, 0xc9, 0x2a, + 0x11, 0xe6, 0xfe, 0x21, 0x94, 0x4f, 0x37, 0x82, 0xa7, 0x67, 0x80, 0xdf, 0x83, 0x7b, 0xcb, 0xdf, + 0x8d, 0x4f, 0xd6, 0xce, 0xf5, 0xcc, 0x0b, 0x94, 0xd3, 0x4d, 0xd0, 0xab, 0x0b, 0xc7, 0x33, 0x5b, + 0xbf, 0xf0, 0xb9, 0x35, 0xdc, 0xa0, 0xb0, 0x40, 0x53, 0xf8, 0x93, 0x04, 0x0e, 0xf2, 0x39, 0xfa, + 0x70, 0xed, 0x7c, 0x49, 0x84, 0xf2, 0xd9, 0xa6, 0x11, 0xe9, 0x29, 0xda, 0xcf, 0x5f, 0x5d, 0x37, + 0xa4, 0xd7, 0xd7, 0x0d, 0xe9, 0xcf, 0xeb, 0x86, 0xf4, 0xeb, 0x4d, 0xa3, 0xf0, 0xfa, 0xa6, 0x51, + 0xf8, 0xe3, 0xa6, 0x51, 0x78, 0xf1, 0x85, 0x8d, 0xfd, 0xab, 0x49, 0x5f, 0xb7, 0xc8, 0xd8, 0xb0, + 0x08, 0x1d, 0x13, 0x6a, 0xe0, 0xbe, 0x75, 0x62, 0x13, 0x63, 0x7a, 0x6a, 0x8c, 0xc9, 0x60, 0x32, + 0x42, 0x94, 0x7f, 0xd2, 0x1f, 0x3e, 0x3e, 0x11, 0xfe, 0xe9, 0x7e, 0xe0, 0x22, 0xda, 0xdf, 0x63, + 0x4f, 0xee, 0xe3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xdd, 0x12, 0x14, 0x2b, 0x56, 0x0c, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/core/03-connection/types/version.go b/modules/core/03-connection/types/version.go index 65200516008..7e700eb760c 100644 --- a/modules/core/03-connection/types/version.go +++ b/modules/core/03-connection/types/version.go @@ -117,8 +117,8 @@ func GetCompatibleVersions() []exported.Version { // IsSupportedVersion returns true if the proposed version has a matching version // identifier and its entire feature set is supported or the version identifier // supports an empty feature set. -func IsSupportedVersion(proposedVersion *Version) bool { - supportedVersion, found := FindSupportedVersion(proposedVersion, GetCompatibleVersions()) +func IsSupportedVersion(supportedVersions []exported.Version, proposedVersion *Version) bool { + supportedVersion, found := FindSupportedVersion(proposedVersion, supportedVersions) if !found { return false } diff --git a/modules/core/03-connection/types/version_test.go b/modules/core/03-connection/types/version_test.go index 29838291dd8..2958c55fa38 100644 --- a/modules/core/03-connection/types/version_test.go +++ b/modules/core/03-connection/types/version_test.go @@ -57,7 +57,7 @@ func TestIsSupportedVersion(t *testing.T) { } for _, tc := range testCases { - require.Equal(t, tc.expPass, types.IsSupportedVersion(tc.version)) + require.Equal(t, tc.expPass, types.IsSupportedVersion(types.GetCompatibleVersions(), tc.version)) } } diff --git a/modules/core/04-channel/keeper/grpc_query.go b/modules/core/04-channel/keeper/grpc_query.go index 9cf680d838a..d0fea656942 100644 --- a/modules/core/04-channel/keeper/grpc_query.go +++ b/modules/core/04-channel/keeper/grpc_query.go @@ -397,18 +397,55 @@ func (q Keeper) UnreceivedPackets(c context.Context, req *types.QueryUnreceivedP ctx := sdk.UnwrapSDKContext(c) - unreceivedSequences := []uint64{} + channel, found := q.GetChannel(sdk.UnwrapSDKContext(c), req.PortId, req.ChannelId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + ) + } - for i, seq := range req.PacketCommitmentSequences { - if seq == 0 { - return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i) - } + var unreceivedSequences []uint64 + switch channel.Ordering { + case types.UNORDERED: + for i, seq := range req.PacketCommitmentSequences { + // filter for invalid sequences to ensure they are not included in the response value. + if seq == 0 { + return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i) + } - // if packet receipt exists on the receiving chain, then packet has already been received - if _, found := q.GetPacketReceipt(ctx, req.PortId, req.ChannelId, seq); !found { - unreceivedSequences = append(unreceivedSequences, seq) + // if the packet receipt does not exist, then it is unreceived + if _, found := q.GetPacketReceipt(ctx, req.PortId, req.ChannelId, seq); !found { + unreceivedSequences = append(unreceivedSequences, seq) + } + } + case types.ORDERED: + nextSequenceRecv, found := q.GetNextSequenceRecv(ctx, req.PortId, req.ChannelId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf( + types.ErrSequenceReceiveNotFound, + "destination port: %s, destination channel: %s", req.PortId, req.ChannelId, + ).Error(), + ) } + for i, seq := range req.PacketCommitmentSequences { + // filter for invalid sequences to ensure they are not included in the response value. + if seq == 0 { + return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i) + } + + // Any sequence greater than or equal to the next sequence to be received is not received. + if seq >= nextSequenceRecv { + unreceivedSequences = append(unreceivedSequences, seq) + } + } + default: + return nil, status.Error( + codes.InvalidArgument, + sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, "channel order %s is not supported", channel.Ordering.String()).Error()) } selfHeight := clienttypes.GetSelfHeight(ctx) diff --git a/modules/core/04-channel/keeper/grpc_query_test.go b/modules/core/04-channel/keeper/grpc_query_test.go index 558621fed2b..1561d05b74f 100644 --- a/modules/core/04-channel/keeper/grpc_query_test.go +++ b/modules/core/04-channel/keeper/grpc_query_test.go @@ -1110,7 +1110,7 @@ func (suite *KeeperTestSuite) TestQueryPacketAcknowledgements() { func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { var ( req *types.QueryUnreceivedPacketsRequest - expSeq = []uint64{} + expSeq = []uint64(nil) ) testCases := []struct { @@ -1156,6 +1156,46 @@ func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { }, false, }, + { + "invalid seq, ordered channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetChannelOrdered() + suite.coordinator.Setup(path) + + req = &types.QueryUnreceivedPacketsRequest{ + PortId: path.EndpointA.ChannelConfig.PortID, + ChannelId: path.EndpointA.ChannelID, + PacketCommitmentSequences: []uint64{0}, + } + }, + false, + }, + { + "channel not found", + func() { + req = &types.QueryUnreceivedPacketsRequest{ + PortId: "invalid-port-id", + ChannelId: "invalid-channel-id", + } + }, + false, + }, + { + "basic success empty packet commitments", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.Setup(path) + + expSeq = []uint64(nil) + req = &types.QueryUnreceivedPacketsRequest{ + PortId: path.EndpointA.ChannelConfig.PortID, + ChannelId: path.EndpointA.ChannelID, + PacketCommitmentSequences: []uint64{}, + } + }, + true, + }, { "basic success unreceived packet commitments", func() { @@ -1181,7 +1221,7 @@ func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetPacketReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 1) - expSeq = []uint64{} + expSeq = []uint64(nil) req = &types.QueryUnreceivedPacketsRequest{ PortId: path.EndpointA.ChannelConfig.PortID, ChannelId: path.EndpointA.ChannelID, @@ -1195,7 +1235,7 @@ func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - expSeq = []uint64{} // reset + expSeq = []uint64(nil) // reset packetCommitments := []uint64{} // set packet receipt for every other sequence @@ -1217,6 +1257,60 @@ func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { }, true, }, + { + "basic success empty packet commitments, ordered channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetChannelOrdered() + suite.coordinator.Setup(path) + + expSeq = []uint64(nil) + req = &types.QueryUnreceivedPacketsRequest{ + PortId: path.EndpointA.ChannelConfig.PortID, + ChannelId: path.EndpointA.ChannelID, + PacketCommitmentSequences: []uint64{}, + } + }, + true, + }, + { + "basic success unreceived packet commitments, ordered channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetChannelOrdered() + suite.coordinator.Setup(path) + + // Note: NextSequenceRecv is set to 1 on channel creation. + expSeq = []uint64{1} + req = &types.QueryUnreceivedPacketsRequest{ + PortId: path.EndpointA.ChannelConfig.PortID, + ChannelId: path.EndpointA.ChannelID, + PacketCommitmentSequences: []uint64{1}, + } + }, + true, + }, + { + "basic success multiple unreceived packet commitments, ordered channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetChannelOrdered() + suite.coordinator.Setup(path) + + // Exercise scenario from issue #1532. NextSequenceRecv is 5, packet commitments provided are 2, 7, 9, 10. + // Packet sequence 2 is already received so only sequences 7, 9, 10 should be considered unreceived. + expSeq = []uint64{7, 9, 10} + packetCommitments := []uint64{2, 7, 9, 10} + suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 5) + + req = &types.QueryUnreceivedPacketsRequest{ + PortId: path.EndpointA.ChannelConfig.PortID, + ChannelId: path.EndpointA.ChannelID, + PacketCommitmentSequences: packetCommitments, + } + }, + true, + }, } for _, tc := range testCases { diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index 7f0816bb1b1..4550a5a7a69 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -98,51 +98,20 @@ func (k Keeper) ChanOpenTry( ctx sdk.Context, order types.Order, connectionHops []string, - portID, - previousChannelID string, + portID string, portCap *capabilitytypes.Capability, counterparty types.Counterparty, counterpartyVersion string, proofInit []byte, proofHeight exported.Height, ) (string, *capabilitytypes.Capability, error) { - var ( - previousChannel types.Channel - previousChannelFound bool - ) - - channelID := previousChannelID - // connection hops only supports a single connection if len(connectionHops) != 1 { return "", nil, sdkerrors.Wrapf(types.ErrTooManyConnectionHops, "expected 1, got %d", len(connectionHops)) } - // empty channel identifier indicates continuing a previous channel handshake - if previousChannelID != "" { - // channel identifier and connection hop length checked on msg.ValidateBasic() - // ensure that the previous channel exists - previousChannel, previousChannelFound = k.GetChannel(ctx, portID, previousChannelID) - if !previousChannelFound { - return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannel, "previous channel does not exist for supplied previous channelID %s", previousChannelID) - } - // previous channel must use the same fields - if !(previousChannel.Ordering == order && - previousChannel.Counterparty.PortId == counterparty.PortId && - previousChannel.Counterparty.ChannelId == "" && - previousChannel.ConnectionHops[0] == connectionHops[0] && // ChanOpenInit will only set a single connection hop - previousChannel.Version == counterpartyVersion) { - return "", nil, sdkerrors.Wrap(types.ErrInvalidChannel, "channel fields mismatch previous channel fields") - } - - if previousChannel.State != types.INIT { - return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannelState, "previous channel state is in %s, expected INIT", previousChannel.State) - } - - } else { - // generate a new channel - channelID = k.GenerateChannelIdentifier(ctx) - } + // generate a new channel + channelID := k.GenerateChannelIdentifier(ctx) if !k.portKeeper.Authenticate(ctx, portCap, portID) { return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) @@ -199,20 +168,9 @@ func (k Keeper) ChanOpenTry( err error ) - if !previousChannelFound { - capKey, err = k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) - if err != nil { - return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) - } - - } else { - // capability initialized in ChanOpenInit - capKey, found = k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) - if !found { - return "", nil, sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, - "capability not found for existing channel, portID (%s) channelID (%s)", portID, channelID, - ) - } + capKey, err = k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) + if err != nil { + return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) } return channelID, capKey, nil @@ -230,18 +188,15 @@ func (k Keeper) WriteOpenTryChannel( counterparty types.Counterparty, version string, ) { - previousChannel, previousChannelFound := k.GetChannel(ctx, portID, channelID) - if !previousChannelFound { - k.SetNextSequenceSend(ctx, portID, channelID, 1) - k.SetNextSequenceRecv(ctx, portID, channelID, 1) - k.SetNextSequenceAck(ctx, portID, channelID, 1) - } + k.SetNextSequenceSend(ctx, portID, channelID, 1) + k.SetNextSequenceRecv(ctx, portID, channelID, 1) + k.SetNextSequenceAck(ctx, portID, channelID, 1) channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version) k.SetChannel(ctx, portID, channelID, channel) - k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", previousChannel.State.String(), "new-state", "TRYOPEN") + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "NONE", "new-state", "TRYOPEN") defer func() { telemetry.IncrCounter(1, "ibc", "channel", "open-try") @@ -267,11 +222,8 @@ func (k Keeper) ChanOpenAck( return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) } - if !(channel.State == types.INIT || channel.State == types.TRYOPEN) { - return sdkerrors.Wrapf( - types.ErrInvalidChannelState, - "channel state should be INIT or TRYOPEN (got %s)", channel.State.String(), - ) + if channel.State != types.INIT { + return sdkerrors.Wrapf(types.ErrInvalidChannelState, "channel state should be INIT (got %s)", channel.State.String()) } if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { diff --git a/modules/core/04-channel/keeper/handshake_test.go b/modules/core/04-channel/keeper/handshake_test.go index 57a88b9999e..967978e498c 100644 --- a/modules/core/04-channel/keeper/handshake_test.go +++ b/modules/core/04-channel/keeper/handshake_test.go @@ -143,10 +143,9 @@ func (suite *KeeperTestSuite) TestChanOpenInit() { // ChanOpenTry can succeed. func (suite *KeeperTestSuite) TestChanOpenTry() { var ( - path *ibctesting.Path - previousChannelID string - portCap *capabilitytypes.Capability - heightDiff uint64 + path *ibctesting.Path + portCap *capabilitytypes.Capability + heightDiff uint64 ) testCases := []testCase{ @@ -158,34 +157,6 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { suite.chainB.CreatePortCapability(suite.chainB.GetSimApp().ScopedIBCMockKeeper, ibctesting.MockPort) portCap = suite.chainB.GetPortCapability(ibctesting.MockPort) }, true}, - {"success with crossing hello", func() { - suite.coordinator.SetupConnections(path) - path.SetChannelOrdered() - err := suite.coordinator.ChanOpenInitOnBothChains(path) - suite.Require().NoError(err) - - previousChannelID = path.EndpointB.ChannelID - portCap = suite.chainB.GetPortCapability(ibctesting.MockPort) - }, true}, - {"previous channel with invalid version, crossing hello", func() { - suite.coordinator.SetupConnections(path) - path.SetChannelOrdered() - - // modify channel version - path.EndpointA.ChannelConfig.Version = "invalid version" - - err := suite.coordinator.ChanOpenInitOnBothChains(path) - suite.Require().NoError(err) - - previousChannelID = path.EndpointB.ChannelID - portCap = suite.chainB.GetPortCapability(ibctesting.MockPort) - }, false}, - {"previous channel with invalid state", func() { - suite.coordinator.SetupConnections(path) - - // make previous channel have wrong ordering - path.EndpointA.ChanOpenInit() - }, false}, {"connection doesn't exist", func() { path.EndpointA.ConnectionID = ibctesting.FirstConnectionID path.EndpointB.ConnectionID = ibctesting.FirstConnectionID @@ -268,7 +239,6 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset heightDiff = 0 // must be explicitly changed in malleate - previousChannelID = "" path = ibctesting.NewPath(suite.chainA, suite.chainB) tc.malleate() @@ -286,7 +256,7 @@ func (suite *KeeperTestSuite) TestChanOpenTry() { channelID, cap, err := suite.chainB.App.GetIBCKeeper().ChannelKeeper.ChanOpenTry( suite.chainB.GetContext(), types.ORDERED, []string{path.EndpointB.ConnectionID}, - path.EndpointB.ChannelConfig.PortID, previousChannelID, portCap, counterparty, path.EndpointA.ChannelConfig.Version, + path.EndpointB.ChannelConfig.PortID, portCap, counterparty, path.EndpointA.ChannelConfig.Version, proof, malleateHeight(proofHeight, heightDiff), ) @@ -352,7 +322,7 @@ func (suite *KeeperTestSuite) TestChanOpenAck() { channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, {"channel doesn't exist", func() {}, false}, - {"channel state is not INIT or TRYOPEN", func() { + {"channel state is not INIT", func() { // create fully open channels on both chains suite.coordinator.Setup(path) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 803250593c0..c504fa99e13 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -361,7 +361,7 @@ func (k Keeper) WriteAcknowledgement( // log that a packet acknowledgement has been written k.Logger(ctx).Info( "acknowledgement written", - "sequence", packet.GetSequence(), + "sequence", strconv.FormatUint(packet.GetSequence(), 10), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), diff --git a/modules/core/04-channel/types/msgs.go b/modules/core/04-channel/types/msgs.go index 6bc2a490f31..13475796a8e 100644 --- a/modules/core/04-channel/types/msgs.go +++ b/modules/core/04-channel/types/msgs.go @@ -66,7 +66,7 @@ var _ sdk.Msg = &MsgChannelOpenTry{} // It is left as an argument for go API backwards compatibility. // nolint:interfacer func NewMsgChannelOpenTry( - portID, previousChannelID, version string, channelOrder Order, connectionHops []string, + portID, version string, channelOrder Order, connectionHops []string, counterpartyPortID, counterpartyChannelID, counterpartyVersion string, proofInit []byte, proofHeight clienttypes.Height, signer string, ) *MsgChannelOpenTry { @@ -74,7 +74,6 @@ func NewMsgChannelOpenTry( channel := NewChannel(TRYOPEN, channelOrder, counterparty, connectionHops, version) return &MsgChannelOpenTry{ PortId: portID, - PreviousChannelId: previousChannelID, Channel: channel, CounterpartyVersion: counterpartyVersion, ProofInit: proofInit, @@ -89,9 +88,7 @@ func (msg MsgChannelOpenTry) ValidateBasic() error { return sdkerrors.Wrap(err, "invalid port ID") } if msg.PreviousChannelId != "" { - if !IsValidChannelID(msg.PreviousChannelId) { - return sdkerrors.Wrap(ErrInvalidChannelIdentifier, "invalid previous channel ID") - } + return sdkerrors.Wrap(ErrInvalidChannelIdentifier, "previous channel identifier must be empty, this field has been deprecated as crossing hellos are no longer supported") } if len(msg.ProofInit) == 0 { return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") diff --git a/modules/core/04-channel/types/msgs_test.go b/modules/core/04-channel/types/msgs_test.go index 8beed13d3ef..99c6cf67bff 100644 --- a/modules/core/04-channel/types/msgs_test.go +++ b/modules/core/04-channel/types/msgs_test.go @@ -78,8 +78,7 @@ type TypesTestSuite struct { func (suite *TypesTestSuite) SetupTest() { app := simapp.Setup(false) db := dbm.NewMemDB() - dblog := log.NewNopLogger() - store := rootmulti.NewStore(db, dblog) + store := rootmulti.NewStore(db, log.NewNopLogger()) storeKey := storetypes.NewKVStoreKey("iavlStoreKey") store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) @@ -153,25 +152,23 @@ func (suite *TypesTestSuite) TestMsgChannelOpenTryValidateBasic() { msg *types.MsgChannelOpenTry expPass bool }{ - {"", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, - {"too short port id", types.NewMsgChannelOpenTry(invalidShortPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"too long port id", types.NewMsgChannelOpenTry(invalidLongPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"port id contains non-alpha", types.NewMsgChannelOpenTry(invalidPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"too short channel id", types.NewMsgChannelOpenTry(portid, invalidShortChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"too long channel id", types.NewMsgChannelOpenTry(portid, invalidLongChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"channel id contains non-alpha", types.NewMsgChannelOpenTry(portid, invalidChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, "", suite.proof, height, addr), true}, - {"proof height is zero", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, - {"invalid channel order", types.NewMsgChannelOpenTry(portid, chanid, version, types.Order(4), connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"connection hops more than 1 ", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"too short connection id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidShortConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"too long connection id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidLongConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"connection id contains non-alpha", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, []string{invalidConnection}, cpportid, cpchanid, version, suite.proof, height, addr), false}, - {"", types.NewMsgChannelOpenTry(portid, chanid, "", types.UNORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, - {"invalid counterparty port id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, invalidPort, cpchanid, version, suite.proof, height, addr), false}, - {"invalid counterparty channel id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, cpportid, invalidChannel, version, suite.proof, height, addr), false}, - {"empty proof", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, cpportid, cpchanid, version, emptyProof, height, addr), false}, - {"channel not in TRYOPEN state", &types.MsgChannelOpenTry{portid, chanid, initChannel, version, suite.proof, height, addr}, false}, + {"", types.NewMsgChannelOpenTry(portid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenTry(invalidShortPort, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenTry(invalidLongPort, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenTry(invalidPort, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, version, types.ORDERED, connHops, cpportid, cpchanid, "", suite.proof, height, addr), true}, + {"proof height is zero", types.NewMsgChannelOpenTry(portid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"invalid channel order", types.NewMsgChannelOpenTry(portid, version, types.Order(4), connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection hops more than 1 ", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, invalidConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too short connection id", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, invalidShortConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long connection id", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, invalidLongConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection id contains non-alpha", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, []string{invalidConnection}, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, "", types.UNORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"invalid counterparty port id", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, connHops, invalidPort, cpchanid, version, suite.proof, height, addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, connHops, cpportid, invalidChannel, version, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelOpenTry(portid, version, types.UNORDERED, connHops, cpportid, cpchanid, version, emptyProof, height, addr), false}, + {"channel not in TRYOPEN state", &types.MsgChannelOpenTry{portid, "", initChannel, version, suite.proof, height, addr}, false}, + {"previous channel id is not empty", &types.MsgChannelOpenTry{portid, chanid, initChannel, version, suite.proof, height, addr}, false}, } for _, tc := range testCases { diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index 4c8d176bd15..6ad733decee 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -160,9 +160,8 @@ func (m *MsgChannelOpenInitResponse) GetVersion() string { // value will be ignored by core IBC. type MsgChannelOpenTry struct { PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` - // in the case of crossing hello's, when both chains call OpenInit, we need - // the channel identifier of the previous channel in state INIT - PreviousChannelId string `protobuf:"bytes,2,opt,name=previous_channel_id,json=previousChannelId,proto3" json:"previous_channel_id,omitempty" yaml:"previous_channel_id"` + // Deprecated: this field is unused. Crossing hello's are no longer supported in core IBC. + PreviousChannelId string `protobuf:"bytes,2,opt,name=previous_channel_id,json=previousChannelId,proto3" json:"previous_channel_id,omitempty" yaml:"previous_channel_id"` // Deprecated: Do not use. // NOTE: the version field within the channel has been deprecated. Its value will be ignored by core IBC. Channel Channel `protobuf:"bytes,3,opt,name=channel,proto3" json:"channel"` CounterpartyVersion string `protobuf:"bytes,4,opt,name=counterparty_version,json=counterpartyVersion,proto3" json:"counterparty_version,omitempty" yaml:"counterparty_version"` @@ -918,88 +917,88 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ - // 1294 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0xe2, 0x56, - 0x17, 0xc6, 0x40, 0x20, 0x39, 0xe4, 0x4d, 0x88, 0x49, 0x32, 0xc4, 0x24, 0x98, 0xd7, 0x8b, 0x49, - 0x94, 0x2a, 0x30, 0xc9, 0x64, 0x54, 0x4d, 0x54, 0xa9, 0x0a, 0x94, 0x51, 0xa3, 0x36, 0x1f, 0x32, - 0xa4, 0x52, 0xd3, 0xaa, 0x08, 0xcc, 0x1d, 0x62, 0x01, 0x36, 0xb5, 0x0d, 0x33, 0xfc, 0x83, 0x51, - 0x56, 0xb3, 0x1e, 0x29, 0xd2, 0x54, 0x5d, 0x55, 0x5d, 0x4c, 0x7f, 0xc6, 0x2c, 0x67, 0xd5, 0x56, - 0x5d, 0xa0, 0x2a, 0xd9, 0x74, 0xcd, 0x2f, 0xa8, 0x7c, 0x7d, 0x6d, 0x0c, 0xd8, 0x8a, 0x33, 0x93, - 0x64, 0xba, 0xf3, 0xbd, 0xe7, 0xb9, 0xe7, 0x9c, 0xfb, 0x9c, 0xe7, 0x7e, 0x19, 0x96, 0xc5, 0x8a, - 0x90, 0x11, 0x64, 0x05, 0x65, 0x84, 0xd3, 0xb2, 0x24, 0xa1, 0x46, 0xa6, 0xb3, 0x99, 0xd1, 0x9e, - 0xa7, 0x5b, 0x8a, 0xac, 0xc9, 0x74, 0x4c, 0xac, 0x08, 0x69, 0xdd, 0x9a, 0x26, 0xd6, 0x74, 0x67, - 0x93, 0x99, 0xaf, 0xc9, 0x35, 0x19, 0xdb, 0x33, 0xfa, 0x97, 0x01, 0x65, 0xd8, 0x81, 0xa3, 0x86, - 0x88, 0x24, 0x4d, 0xf7, 0x63, 0x7c, 0x11, 0xc0, 0xff, 0x9d, 0x22, 0x99, 0x6e, 0x31, 0x84, 0xfb, - 0x89, 0x02, 0x7a, 0x5f, 0xad, 0xe5, 0x8c, 0xce, 0xc3, 0x16, 0x92, 0xf6, 0x24, 0x51, 0xa3, 0x3f, - 0x81, 0x70, 0x4b, 0x56, 0xb4, 0x92, 0x58, 0x8d, 0x53, 0x29, 0x6a, 0x6d, 0x2a, 0x4b, 0xf7, 0x7b, - 0xec, 0x4c, 0xb7, 0xdc, 0x6c, 0xec, 0x70, 0xc4, 0xc0, 0xf1, 0x21, 0xfd, 0x6b, 0xaf, 0x4a, 0x7f, - 0x06, 0x61, 0xe2, 0x34, 0xee, 0x4f, 0x51, 0x6b, 0x91, 0xad, 0xe5, 0xb4, 0xc3, 0x24, 0xd2, 0x24, - 0x46, 0x36, 0xf8, 0xb6, 0xc7, 0xfa, 0x78, 0x73, 0x08, 0xbd, 0x08, 0x21, 0x55, 0xac, 0x49, 0x48, - 0x89, 0x07, 0xf4, 0x48, 0x3c, 0x69, 0xed, 0x4c, 0xbe, 0x78, 0xcd, 0xfa, 0xfe, 0x79, 0xcd, 0xfa, - 0xb8, 0x06, 0x30, 0xe3, 0x29, 0xf2, 0x48, 0x6d, 0xc9, 0x92, 0x8a, 0xe8, 0x6d, 0x00, 0xe2, 0x6a, - 0x90, 0xed, 0x42, 0xbf, 0xc7, 0xce, 0x19, 0xd9, 0x0e, 0x6c, 0x1c, 0x3f, 0x45, 0x1a, 0x7b, 0x55, - 0x3a, 0x0e, 0xe1, 0x0e, 0x52, 0x54, 0x51, 0x96, 0x70, 0xce, 0x53, 0xbc, 0xd9, 0xe4, 0x7e, 0x0f, - 0xc0, 0xdc, 0x70, 0xb8, 0xa2, 0xd2, 0xbd, 0x1e, 0x21, 0x07, 0x10, 0x6b, 0x29, 0xa8, 0x23, 0xca, - 0x6d, 0xb5, 0x64, 0xcb, 0x0d, 0x07, 0xca, 0x26, 0xfb, 0x3d, 0x96, 0x21, 0x03, 0xc7, 0x41, 0x1c, - 0x3f, 0x67, 0xf6, 0xe6, 0xac, 0x64, 0x6d, 0x04, 0x07, 0xae, 0x4f, 0x30, 0x0f, 0xf3, 0x82, 0xdc, - 0x96, 0x34, 0xa4, 0xb4, 0xca, 0x8a, 0xd6, 0x2d, 0x99, 0xf3, 0x0e, 0xe2, 0x74, 0xd8, 0x7e, 0x8f, - 0x4d, 0x10, 0xaa, 0x1c, 0x50, 0x1c, 0x1f, 0xb3, 0x77, 0x7f, 0x63, 0xf4, 0xea, 0xa4, 0xb7, 0x14, - 0x59, 0x7e, 0x5a, 0x12, 0x25, 0x51, 0x8b, 0x4f, 0xa4, 0xa8, 0xb5, 0x69, 0x3b, 0xe9, 0x03, 0x1b, - 0xc7, 0x4f, 0xe1, 0x06, 0x56, 0xd5, 0x09, 0x4c, 0x1b, 0x96, 0x53, 0x24, 0xd6, 0x4e, 0xb5, 0x78, - 0x08, 0x4f, 0x86, 0xb1, 0x4d, 0xc6, 0x50, 0x6f, 0x67, 0x33, 0xfd, 0x25, 0x46, 0x64, 0x13, 0xfa, - 0x54, 0xfa, 0x3d, 0x36, 0x66, 0xf7, 0x6b, 0x8c, 0xe6, 0xf8, 0x08, 0x6e, 0x1a, 0x48, 0x9b, 0x8c, - 0xc2, 0x2e, 0x32, 0x7a, 0x04, 0x4b, 0x63, 0x75, 0xb5, 0x54, 0x64, 0xd3, 0x03, 0x35, 0xac, 0x87, - 0x3f, 0xc6, 0xf4, 0xb0, 0x2b, 0xd4, 0xaf, 0xa7, 0x87, 0x61, 0x89, 0xfa, 0x3d, 0x4a, 0xf4, 0x04, - 0xee, 0x0d, 0x55, 0xc4, 0xe6, 0x02, 0xaf, 0x94, 0x2c, 0xd7, 0xef, 0xb1, 0x49, 0x87, 0xd2, 0xd9, - 0xfd, 0x2d, 0xd8, 0x2d, 0x03, 0x45, 0xdd, 0x86, 0x26, 0x36, 0xc1, 0x28, 0x75, 0x49, 0x53, 0xba, - 0x44, 0x12, 0xf3, 0xfd, 0x1e, 0x1b, 0xb5, 0x97, 0x4e, 0x53, 0xba, 0x1c, 0x3f, 0x89, 0xbf, 0xf5, - 0x55, 0xf5, 0x71, 0x05, 0x91, 0x18, 0x15, 0xc4, 0xae, 0x50, 0x37, 0x05, 0xc1, 0xfd, 0xea, 0x87, - 0x85, 0x61, 0x6b, 0x4e, 0x96, 0x9e, 0x8a, 0x4a, 0xf3, 0x2e, 0x4a, 0x6f, 0x51, 0x59, 0x16, 0xea, - 0xb8, 0xd8, 0x0e, 0x54, 0x96, 0x85, 0xba, 0x49, 0xa5, 0x2e, 0xc8, 0x51, 0x2a, 0x83, 0xb7, 0x42, - 0xe5, 0x84, 0x0b, 0x95, 0x2c, 0xac, 0x38, 0x92, 0x65, 0xd1, 0xf9, 0x8a, 0x82, 0xd8, 0x00, 0x91, - 0x6b, 0xc8, 0x2a, 0xba, 0xfe, 0x41, 0xf3, 0x7e, 0x64, 0x5e, 0x7d, 0xc0, 0xac, 0x40, 0xc2, 0x21, - 0x37, 0x2b, 0xf7, 0x37, 0x7e, 0x58, 0x1c, 0xb1, 0xdf, 0xa1, 0x16, 0x86, 0xb7, 0xda, 0xc0, 0x7b, - 0x6e, 0xb5, 0x77, 0x2b, 0x87, 0x14, 0x24, 0x9d, 0x09, 0xb3, 0x38, 0x7d, 0xe9, 0x87, 0xff, 0xed, - 0xab, 0x35, 0x1e, 0x09, 0x9d, 0xa3, 0xb2, 0x50, 0x47, 0x1a, 0xfd, 0x18, 0x42, 0x2d, 0xfc, 0x85, - 0x99, 0x8c, 0x6c, 0x25, 0x1c, 0xcf, 0x38, 0x03, 0x4c, 0x8e, 0x38, 0x32, 0x80, 0x7e, 0x02, 0x51, - 0x23, 0x5d, 0x41, 0x6e, 0x36, 0x45, 0xad, 0x89, 0x24, 0x0d, 0xd3, 0x3b, 0x9d, 0x4d, 0xf4, 0x7b, - 0xec, 0x3d, 0xfb, 0x84, 0x06, 0x08, 0x8e, 0x9f, 0xc5, 0x5d, 0x39, 0xab, 0x67, 0x8c, 0xb4, 0xc0, - 0xad, 0x90, 0x16, 0x74, 0x21, 0xed, 0x07, 0xbc, 0xe1, 0x0c, 0x18, 0xb1, 0xce, 0xa6, 0xcf, 0x21, - 0xa4, 0x20, 0xb5, 0xdd, 0x30, 0x98, 0x99, 0xd9, 0x5a, 0x75, 0x64, 0xc6, 0x84, 0xf3, 0x18, 0x5a, - 0xec, 0xb6, 0x10, 0x4f, 0x86, 0xed, 0x04, 0xf5, 0x18, 0xdc, 0x5f, 0x7e, 0x80, 0x7d, 0xb5, 0x56, - 0x14, 0x9b, 0x48, 0x6e, 0xdf, 0x0c, 0xdf, 0x6d, 0x49, 0x41, 0x02, 0x12, 0x3b, 0xa8, 0xea, 0xc6, - 0xf7, 0x00, 0x61, 0xf2, 0x7d, 0x6c, 0xf5, 0xdc, 0x2a, 0xdf, 0x5f, 0x01, 0x2d, 0xa1, 0xe7, 0x5a, - 0x49, 0x45, 0x3f, 0xb6, 0x91, 0x24, 0xa0, 0x92, 0x82, 0x84, 0x0e, 0xe6, 0x3e, 0x98, 0x5d, 0xe9, - 0xf7, 0xd8, 0x25, 0xc3, 0xc3, 0x38, 0x86, 0xe3, 0xa3, 0x7a, 0x67, 0x81, 0xf4, 0xe9, 0xf5, 0xf0, - 0xa0, 0xf8, 0xef, 0xf0, 0x35, 0x9a, 0x70, 0x7b, 0xd3, 0x95, 0x7b, 0x65, 0x5c, 0x41, 0x88, 0xf7, - 0x43, 0x09, 0xaf, 0xa8, 0xff, 0x42, 0x01, 0x3f, 0x85, 0x08, 0x59, 0x56, 0x7a, 0x46, 0x64, 0x73, - 0x5a, 0xec, 0xf7, 0x58, 0x7a, 0x68, 0xcd, 0xe9, 0x46, 0x8e, 0x37, 0xb6, 0x31, 0x23, 0xf7, 0xdb, - 0xdc, 0x9e, 0x9c, 0x2b, 0x3f, 0xf1, 0xa1, 0x95, 0x0f, 0xb9, 0x54, 0xbe, 0x82, 0x6f, 0x11, 0xc3, - 0xb5, 0xb9, 0x69, 0x01, 0xfc, 0xe6, 0xc7, 0xf2, 0xda, 0x15, 0xea, 0x92, 0xfc, 0xac, 0x81, 0xaa, - 0x35, 0x84, 0xf7, 0xab, 0x0f, 0x50, 0xc0, 0x1a, 0xcc, 0x96, 0x87, 0xbd, 0x19, 0x02, 0xe0, 0x47, - 0xbb, 0x07, 0x35, 0xd6, 0x07, 0x56, 0xdd, 0x6a, 0x8c, 0x8d, 0x66, 0x8d, 0x77, 0xf5, 0xc6, 0x47, - 0x3e, 0x82, 0x04, 0xfc, 0x68, 0x1c, 0x61, 0xec, 0x86, 0xeb, 0xb2, 0xfe, 0x0b, 0x05, 0xf4, 0x38, - 0x88, 0x7e, 0x04, 0x29, 0x3e, 0x5f, 0x38, 0x3a, 0x3c, 0x28, 0xe4, 0x4b, 0x7c, 0xbe, 0x70, 0xfc, - 0x75, 0xb1, 0x54, 0xfc, 0xf6, 0x28, 0x5f, 0x3a, 0x3e, 0x28, 0x1c, 0xe5, 0x73, 0x7b, 0x4f, 0xf6, - 0xf2, 0x5f, 0x44, 0x7d, 0xcc, 0xec, 0xd9, 0x79, 0x2a, 0x62, 0xeb, 0xa2, 0x57, 0x61, 0xc9, 0x71, - 0xd8, 0xc1, 0xe1, 0xe1, 0x51, 0x94, 0x62, 0x26, 0xcf, 0xce, 0x53, 0x41, 0xfd, 0x9b, 0xde, 0x80, - 0x65, 0x47, 0x60, 0xe1, 0x38, 0x97, 0xcb, 0x17, 0x0a, 0x51, 0x3f, 0x13, 0x39, 0x3b, 0x4f, 0x85, - 0x49, 0x93, 0x09, 0xbe, 0xf8, 0x39, 0xe9, 0xdb, 0x7a, 0x33, 0x09, 0x81, 0x7d, 0xb5, 0x46, 0xd7, - 0x61, 0x76, 0xf4, 0xb5, 0xef, 0x3c, 0xfb, 0xf1, 0x37, 0x37, 0x93, 0xf1, 0x08, 0xb4, 0x78, 0x3e, - 0x85, 0x99, 0x91, 0x87, 0xf4, 0x7d, 0x0f, 0x2e, 0x8a, 0x4a, 0x97, 0x49, 0x7b, 0xc3, 0xb9, 0x44, - 0xd2, 0x6f, 0xc4, 0x5e, 0x22, 0xed, 0x0a, 0x75, 0x4f, 0x91, 0x6c, 0x2f, 0x03, 0x5a, 0x03, 0xda, - 0xe1, 0x55, 0xb0, 0xee, 0xc1, 0x0b, 0xc1, 0x32, 0x5b, 0xde, 0xb1, 0x56, 0x54, 0x09, 0xa2, 0x63, - 0x97, 0xe7, 0xb5, 0x2b, 0xfc, 0x58, 0x48, 0xe6, 0x81, 0x57, 0xa4, 0x15, 0xef, 0x19, 0xc4, 0x1c, - 0x2f, 0xbc, 0x5e, 0x1c, 0x99, 0xf3, 0x7c, 0x78, 0x0d, 0xb0, 0x15, 0xf8, 0x7b, 0x00, 0xdb, 0xad, - 0x90, 0x73, 0x73, 0x31, 0xc0, 0x30, 0xeb, 0x57, 0x63, 0x2c, 0xef, 0x05, 0x08, 0x9b, 0x17, 0x20, - 0xd6, 0x6d, 0x18, 0x01, 0x30, 0xab, 0x57, 0x00, 0xec, 0xda, 0x1b, 0x39, 0x9b, 0xef, 0x5f, 0x31, - 0x94, 0xe0, 0xdc, 0xb5, 0xe7, 0x72, 0x9e, 0xd4, 0x61, 0x76, 0xf4, 0x10, 0x70, 0xcd, 0x72, 0x04, - 0xe8, 0xbe, 0x78, 0x5d, 0x36, 0xc9, 0x6c, 0xe1, 0xed, 0x45, 0x92, 0x7a, 0x77, 0x91, 0xa4, 0xfe, - 0xbe, 0x48, 0x52, 0x2f, 0x2f, 0x93, 0xbe, 0x77, 0x97, 0x49, 0xdf, 0x9f, 0x97, 0x49, 0xdf, 0xc9, - 0xe3, 0x9a, 0xa8, 0x9d, 0xb6, 0x2b, 0x69, 0x41, 0x6e, 0x66, 0x04, 0x59, 0x6d, 0xca, 0x6a, 0x46, - 0xac, 0x08, 0x1b, 0x35, 0x39, 0xd3, 0xd9, 0xce, 0x34, 0xe5, 0x6a, 0xbb, 0x81, 0x54, 0xe3, 0xc7, - 0xe3, 0x83, 0xed, 0x0d, 0xf3, 0xdf, 0xa3, 0xd6, 0x6d, 0x21, 0xb5, 0x12, 0xc2, 0xff, 0x1d, 0x1f, - 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x4f, 0x1a, 0x39, 0xd7, 0x06, 0x15, 0x00, 0x00, + // 1295 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xdf, 0x6f, 0xda, 0xd6, + 0x17, 0xc7, 0x40, 0x21, 0x39, 0xf4, 0x9b, 0x10, 0x93, 0xa4, 0xc4, 0x24, 0x98, 0xaf, 0x1f, 0x9a, + 0x28, 0x53, 0xa0, 0x49, 0x53, 0x4d, 0x8d, 0x26, 0x4d, 0x81, 0x51, 0x2d, 0xda, 0x92, 0x20, 0x43, + 0x26, 0x2d, 0x9b, 0x86, 0xc0, 0xdc, 0x12, 0x0b, 0xb0, 0x99, 0x6d, 0x68, 0xf9, 0x0f, 0xaa, 0x3c, + 0xf5, 0xb9, 0x52, 0xa4, 0x4e, 0x7b, 0x9a, 0xf6, 0xd0, 0xfd, 0x19, 0x7d, 0xec, 0xdb, 0xaa, 0x3d, + 0xa0, 0x29, 0x79, 0xd9, 0x33, 0x7f, 0xc1, 0xe4, 0xeb, 0x6b, 0x63, 0xc0, 0x56, 0x9c, 0x36, 0x49, + 0xf7, 0xe6, 0x7b, 0xcf, 0xe7, 0x9e, 0x73, 0xee, 0xe7, 0x7c, 0xee, 0x2f, 0xc3, 0xb2, 0x58, 0x15, + 0x32, 0x82, 0xac, 0xa0, 0x8c, 0x70, 0x52, 0x91, 0x24, 0xd4, 0xcc, 0x74, 0x37, 0x33, 0xda, 0xf3, + 0x74, 0x5b, 0x91, 0x35, 0x99, 0x8e, 0x89, 0x55, 0x21, 0xad, 0x5b, 0xd3, 0xc4, 0x9a, 0xee, 0x6e, + 0x32, 0xf3, 0x75, 0xb9, 0x2e, 0x63, 0x7b, 0x46, 0xff, 0x32, 0xa0, 0x0c, 0x3b, 0x74, 0xd4, 0x14, + 0x91, 0xa4, 0xe9, 0x7e, 0x8c, 0x2f, 0x02, 0xf8, 0xbf, 0x53, 0x24, 0xd3, 0x2d, 0x86, 0x70, 0xbf, + 0x50, 0x40, 0xef, 0xab, 0xf5, 0x9c, 0xd1, 0x79, 0xd8, 0x46, 0xd2, 0x9e, 0x24, 0x6a, 0xf4, 0x67, + 0x10, 0x6e, 0xcb, 0x8a, 0x56, 0x16, 0x6b, 0x71, 0x2a, 0x45, 0xad, 0x4d, 0x67, 0xe9, 0x41, 0x9f, + 0x9d, 0xe9, 0x55, 0x5a, 0xcd, 0x1d, 0x8e, 0x18, 0x38, 0x3e, 0xa4, 0x7f, 0xed, 0xd5, 0xe8, 0x2f, + 0x20, 0x4c, 0x9c, 0xc6, 0xfd, 0x29, 0x6a, 0x2d, 0xb2, 0xb5, 0x9c, 0x76, 0x98, 0x44, 0x9a, 0xc4, + 0xc8, 0x06, 0xdf, 0xf6, 0x59, 0x1f, 0x6f, 0x0e, 0xa1, 0x17, 0x21, 0xa4, 0x8a, 0x75, 0x09, 0x29, + 0xf1, 0x80, 0x1e, 0x89, 0x27, 0xad, 0x9d, 0xa9, 0x17, 0xaf, 0x59, 0xdf, 0x3f, 0xaf, 0x59, 0x1f, + 0xd7, 0x04, 0x66, 0x32, 0x45, 0x1e, 0xa9, 0x6d, 0x59, 0x52, 0x11, 0xbd, 0x0d, 0x40, 0x5c, 0x0d, + 0xb3, 0x5d, 0x18, 0xf4, 0xd9, 0x39, 0x23, 0xdb, 0xa1, 0x8d, 0xe3, 0xa7, 0x49, 0x63, 0xaf, 0x46, + 0xc7, 0x21, 0xdc, 0x45, 0x8a, 0x2a, 0xca, 0x12, 0xce, 0x79, 0x9a, 0x37, 0x9b, 0xdc, 0xfb, 0x00, + 0xcc, 0x8d, 0x86, 0x2b, 0x29, 0xbd, 0xab, 0x11, 0x52, 0x80, 0x58, 0x5b, 0x41, 0x5d, 0x51, 0xee, + 0xa8, 0x65, 0x5b, 0x6e, 0x38, 0x50, 0x36, 0x35, 0xe8, 0xb3, 0x0c, 0x19, 0x38, 0x09, 0xe2, 0xe2, + 0x14, 0x3f, 0x67, 0xf6, 0xe7, 0xac, 0x74, 0x6d, 0x14, 0x07, 0xae, 0x4e, 0x31, 0x0f, 0xf3, 0x82, + 0xdc, 0x91, 0x34, 0xa4, 0xb4, 0x2b, 0x8a, 0xd6, 0x2b, 0x9b, 0x33, 0x0f, 0xe2, 0x84, 0xd8, 0x41, + 0x9f, 0x4d, 0x10, 0xb2, 0x1c, 0x50, 0x1c, 0x1f, 0xb3, 0x77, 0x7f, 0x67, 0xf4, 0xea, 0xb4, 0xb7, + 0x15, 0x59, 0x7e, 0x5a, 0x16, 0x25, 0x51, 0x8b, 0xdf, 0x49, 0x51, 0x6b, 0x77, 0xed, 0xb4, 0x0f, + 0x6d, 0x1c, 0x3f, 0x8d, 0x1b, 0x58, 0x57, 0xc7, 0x70, 0xd7, 0xb0, 0x9c, 0x20, 0xb1, 0x7e, 0xa2, + 0xc5, 0x43, 0x78, 0x32, 0x8c, 0x6d, 0x32, 0x86, 0x7e, 0xbb, 0x9b, 0xe9, 0xaf, 0x31, 0x22, 0x9b, + 0xd0, 0xa7, 0x32, 0xe8, 0xb3, 0x31, 0xbb, 0x5f, 0x63, 0x34, 0xc7, 0x47, 0x70, 0xd3, 0x40, 0xda, + 0x84, 0x14, 0x76, 0x11, 0xd2, 0x23, 0x58, 0x9a, 0xa8, 0xac, 0xa5, 0x23, 0x9b, 0x22, 0xa8, 0x51, + 0x45, 0xfc, 0x39, 0xa1, 0x88, 0x5d, 0xa1, 0x71, 0x35, 0x45, 0x8c, 0x8a, 0xd4, 0xef, 0x51, 0xa4, + 0xc7, 0x70, 0x6f, 0xa4, 0x22, 0x36, 0x17, 0x78, 0xad, 0x64, 0xb9, 0x41, 0x9f, 0x4d, 0x3a, 0x94, + 0xce, 0xee, 0x6f, 0xc1, 0x6e, 0x19, 0x2a, 0xea, 0x26, 0x34, 0xb1, 0x09, 0x46, 0xa9, 0xcb, 0x9a, + 0xd2, 0x23, 0x92, 0x98, 0x1f, 0xf4, 0xd9, 0xa8, 0xbd, 0x74, 0x9a, 0xd2, 0xe3, 0xf8, 0x29, 0xfc, + 0xad, 0xaf, 0xab, 0x4f, 0x2b, 0x88, 0xc4, 0xb8, 0x20, 0x76, 0x85, 0x86, 0x29, 0x08, 0xee, 0x77, + 0x3f, 0x2c, 0x8c, 0x5a, 0x73, 0xb2, 0xf4, 0x54, 0x54, 0x5a, 0xb7, 0x51, 0x7a, 0x8b, 0xca, 0x8a, + 0xd0, 0xc0, 0xc5, 0x76, 0xa0, 0xb2, 0x22, 0x34, 0x4c, 0x2a, 0x75, 0x41, 0x8e, 0x53, 0x19, 0xbc, + 0x11, 0x2a, 0xef, 0xb8, 0x50, 0xc9, 0xc2, 0x8a, 0x23, 0x59, 0x16, 0x9d, 0xaf, 0x28, 0x88, 0x0d, + 0x11, 0xb9, 0xa6, 0xac, 0xa2, 0xab, 0x1f, 0x35, 0x1f, 0x46, 0xe6, 0xe5, 0x47, 0xcc, 0x0a, 0x24, + 0x1c, 0x72, 0xb3, 0x72, 0x7f, 0xe3, 0x87, 0xc5, 0x31, 0xfb, 0x2d, 0x6a, 0x61, 0x74, 0xab, 0x0d, + 0x7c, 0xe0, 0x56, 0x7b, 0xbb, 0x72, 0x48, 0x41, 0xd2, 0x99, 0x30, 0x8b, 0xd3, 0x97, 0x7e, 0xf8, + 0xdf, 0xbe, 0x5a, 0xe7, 0x91, 0xd0, 0x2d, 0x54, 0x84, 0x06, 0xd2, 0xe8, 0xc7, 0x10, 0x6a, 0xe3, + 0x2f, 0xcc, 0x64, 0x64, 0x2b, 0xe1, 0x78, 0xc6, 0x19, 0x60, 0x72, 0xc4, 0x91, 0x01, 0xf4, 0x13, + 0x88, 0x1a, 0xe9, 0x0a, 0x72, 0xab, 0x25, 0x6a, 0x2d, 0x24, 0x69, 0x98, 0xde, 0xbb, 0xd9, 0xc4, + 0xa0, 0xcf, 0xde, 0xb3, 0x4f, 0x68, 0x88, 0xe0, 0xf8, 0x59, 0xdc, 0x95, 0xb3, 0x7a, 0x26, 0x48, + 0x0b, 0xdc, 0x08, 0x69, 0x41, 0x17, 0xd2, 0x7e, 0xc2, 0x1b, 0xce, 0x90, 0x11, 0xeb, 0x6c, 0xfa, + 0x12, 0x42, 0x0a, 0x52, 0x3b, 0x4d, 0x83, 0x99, 0x99, 0xad, 0x55, 0x47, 0x66, 0x4c, 0x38, 0x8f, + 0xa1, 0xa5, 0x5e, 0x1b, 0xf1, 0x64, 0xd8, 0x4e, 0x50, 0x8f, 0xc1, 0xfd, 0xe5, 0x07, 0xd8, 0x57, + 0xeb, 0x25, 0xb1, 0x85, 0xe4, 0xce, 0xf5, 0xf0, 0xdd, 0x91, 0x14, 0x24, 0x20, 0xb1, 0x8b, 0x6a, + 0x6e, 0x7c, 0x0f, 0x11, 0x26, 0xdf, 0x47, 0x56, 0xcf, 0x8d, 0xf2, 0xfd, 0x0d, 0xd0, 0x12, 0x7a, + 0xae, 0x95, 0x55, 0xf4, 0x73, 0x07, 0x49, 0x02, 0x2a, 0x2b, 0x48, 0xe8, 0x62, 0xee, 0x83, 0xd9, + 0x95, 0x41, 0x9f, 0x5d, 0x32, 0x3c, 0x4c, 0x62, 0x38, 0x3e, 0xaa, 0x77, 0x16, 0x49, 0x9f, 0x5e, + 0x0f, 0x0f, 0x8a, 0xff, 0x01, 0x5f, 0xa4, 0x09, 0xb7, 0xd7, 0x5d, 0xb9, 0x57, 0xc6, 0x15, 0x84, + 0x78, 0x3f, 0x94, 0xf0, 0x8a, 0xfa, 0x2f, 0x14, 0xf0, 0x73, 0x88, 0x90, 0x65, 0xa5, 0x67, 0x44, + 0x36, 0xa7, 0xc5, 0x41, 0x9f, 0xa5, 0x47, 0xd6, 0x9c, 0x6e, 0xe4, 0x78, 0x63, 0x1b, 0x33, 0x72, + 0xbf, 0xc9, 0xed, 0xc9, 0xb9, 0xf2, 0x77, 0x3e, 0xb6, 0xf2, 0x21, 0x97, 0xca, 0x57, 0xf1, 0x2d, + 0x62, 0xb4, 0x36, 0xd7, 0x2d, 0x80, 0x3f, 0xfc, 0x58, 0x5e, 0xbb, 0x42, 0x43, 0x92, 0x9f, 0x35, + 0x51, 0xad, 0x8e, 0xf0, 0x7e, 0xf5, 0x11, 0x0a, 0x58, 0x83, 0xd9, 0xca, 0xa8, 0x37, 0x43, 0x00, + 0xfc, 0x78, 0xf7, 0xb0, 0xc6, 0xfa, 0xc0, 0x9a, 0x5b, 0x8d, 0xb1, 0xd1, 0xac, 0xf1, 0xae, 0xde, + 0xf8, 0xc4, 0x47, 0x90, 0x80, 0x9f, 0x8d, 0x63, 0x8c, 0x5d, 0x73, 0x5d, 0xd6, 0x7f, 0xa3, 0x80, + 0x9e, 0x04, 0xd1, 0x8f, 0x20, 0xc5, 0xe7, 0x8b, 0x85, 0xc3, 0x83, 0x62, 0xbe, 0xcc, 0xe7, 0x8b, + 0x47, 0xdf, 0x96, 0xca, 0xa5, 0xef, 0x0b, 0xf9, 0xf2, 0xd1, 0x41, 0xb1, 0x90, 0xcf, 0xed, 0x3d, + 0xd9, 0xcb, 0x7f, 0x15, 0xf5, 0x31, 0xb3, 0xa7, 0x67, 0xa9, 0x88, 0xad, 0x8b, 0x5e, 0x85, 0x25, + 0xc7, 0x61, 0x07, 0x87, 0x87, 0x85, 0x28, 0xc5, 0x4c, 0x9d, 0x9e, 0xa5, 0x82, 0xfa, 0x37, 0xbd, + 0x01, 0xcb, 0x8e, 0xc0, 0xe2, 0x51, 0x2e, 0x97, 0x2f, 0x16, 0xa3, 0x7e, 0x26, 0x72, 0x7a, 0x96, + 0x0a, 0x93, 0x26, 0x13, 0x7c, 0xf1, 0x6b, 0xd2, 0xb7, 0xf5, 0x66, 0x0a, 0x02, 0xfb, 0x6a, 0x9d, + 0x6e, 0xc0, 0xec, 0xf8, 0x7b, 0xdf, 0x79, 0xf6, 0x93, 0xaf, 0x6e, 0x26, 0xe3, 0x11, 0x68, 0xf1, + 0x7c, 0x02, 0x33, 0x63, 0x4f, 0xe9, 0xfb, 0x1e, 0x5c, 0x94, 0x94, 0x1e, 0x93, 0xf6, 0x86, 0x73, + 0x89, 0xa4, 0xdf, 0x88, 0xbd, 0x44, 0xda, 0x15, 0x1a, 0x9e, 0x22, 0xd9, 0x5e, 0x06, 0xb4, 0x06, + 0xb4, 0xc3, 0xab, 0x60, 0xdd, 0x83, 0x17, 0x82, 0x65, 0xb6, 0xbc, 0x63, 0xad, 0xa8, 0x12, 0x44, + 0x27, 0x2e, 0xcf, 0x6b, 0x97, 0xf8, 0xb1, 0x90, 0xcc, 0x03, 0xaf, 0x48, 0x2b, 0xde, 0x33, 0x88, + 0x39, 0x5e, 0x78, 0xbd, 0x38, 0x32, 0xe7, 0xf9, 0xf0, 0x0a, 0x60, 0x2b, 0xf0, 0x8f, 0x00, 0xb6, + 0x5b, 0x21, 0xe7, 0xe6, 0x62, 0x88, 0x61, 0xd6, 0x2f, 0xc7, 0x58, 0xde, 0x8b, 0x10, 0x36, 0x2f, + 0x40, 0xac, 0xdb, 0x30, 0x02, 0x60, 0x56, 0x2f, 0x01, 0xd8, 0xb5, 0x37, 0x76, 0x36, 0xdf, 0xbf, + 0x64, 0x28, 0xc1, 0xb9, 0x6b, 0xcf, 0xe5, 0x3c, 0x69, 0xc0, 0xec, 0xf8, 0x21, 0xe0, 0x9a, 0xe5, + 0x18, 0xd0, 0x7d, 0xf1, 0xba, 0x6c, 0x92, 0xd9, 0xe2, 0xdb, 0xf3, 0x24, 0xf5, 0xee, 0x3c, 0x49, + 0xfd, 0x7d, 0x9e, 0xa4, 0x5e, 0x5e, 0x24, 0x7d, 0xef, 0x2e, 0x92, 0xbe, 0xf7, 0x17, 0x49, 0xdf, + 0xf1, 0xe3, 0xba, 0xa8, 0x9d, 0x74, 0xaa, 0x69, 0x41, 0x6e, 0x65, 0x04, 0x59, 0x6d, 0xc9, 0x6a, + 0x46, 0xac, 0x0a, 0x1b, 0x75, 0x39, 0xd3, 0xdd, 0xce, 0xb4, 0xe4, 0x5a, 0xa7, 0x89, 0x54, 0xe3, + 0xd7, 0xe3, 0x83, 0xed, 0x0d, 0xf3, 0xef, 0xa3, 0xd6, 0x6b, 0x23, 0xb5, 0x1a, 0xc2, 0x7f, 0x1e, + 0x1f, 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x58, 0xd3, 0x8a, 0x27, 0x08, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/core/04-channel/types/version.go b/modules/core/04-channel/types/version.go deleted file mode 100644 index a2696d291ed..00000000000 --- a/modules/core/04-channel/types/version.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import "strings" - -const ChannelVersionDelimiter = ":" - -// SplitChannelVersion middleware version will split the channel version string -// into the outermost middleware version and the underlying app version. -// It will use the default delimiter `:` for middleware versions. -// In case there's no delimeter, this function returns an empty string for the middleware version (first return argument), -// and the full input as the second underlying app version. -func SplitChannelVersion(version string) (middlewareVersion, appVersion string) { - // only split out the first middleware version - splitVersions := strings.Split(version, ChannelVersionDelimiter) - if len(splitVersions) == 1 { - return "", version - } - middlewareVersion = splitVersions[0] - appVersion = strings.Join(splitVersions[1:], ChannelVersionDelimiter) - return -} - -// MergeChannelVersions merges the provided versions together with the channel version delimiter -// the versions should be passed in from the highest-level middleware to the base application -func MergeChannelVersions(versions ...string) string { - return strings.Join(versions, ChannelVersionDelimiter) -} diff --git a/modules/core/04-channel/types/version_test.go b/modules/core/04-channel/types/version_test.go deleted file mode 100644 index d735f162ae1..00000000000 --- a/modules/core/04-channel/types/version_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types" -) - -func TestSplitVersions(t *testing.T) { - testCases := []struct { - name string - version string - mwVersion string - appVersion string - }{ - { - "single wrapped middleware", - "fee29-1:ics20-1", - "fee29-1", - "ics20-1", - }, - { - "multiple wrapped middleware", - "fee29-1:whitelist:ics20-1", - "fee29-1", - "whitelist:ics20-1", - }, - { - "no middleware", - "ics20-1", - "", - "ics20-1", - }, - } - - for _, tc := range testCases { - mwVersion, appVersion := types.SplitChannelVersion(tc.version) - require.Equal(t, tc.mwVersion, mwVersion, "middleware version is unexpected for case: %s", tc.name) - require.Equal(t, tc.appVersion, appVersion, "app version is unexpected for case: %s", tc.name) - } -} - -func TestMergeVersions(t *testing.T) { - testCases := []struct { - name string - versions []string - merged string - }{ - { - "single version", - []string{"ics20-1"}, - "ics20-1", - }, - { - "empty version", - []string{}, - "", - }, - { - "two versions", - []string{"fee29-1", "ics20-1"}, - "fee29-1:ics20-1", - }, - { - "multiple versions", - []string{"fee29-1", "whitelist", "ics20-1"}, - "fee29-1:whitelist:ics20-1", - }, - } - - for _, tc := range testCases { - actual := types.MergeChannelVersions(tc.versions...) - require.Equal(t, tc.merged, actual, "merged versions string does not equal expected value") - } -} diff --git a/modules/core/23-commitment/types/commitment_test.go b/modules/core/23-commitment/types/commitment_test.go index 814675d67f8..870fa099587 100644 --- a/modules/core/23-commitment/types/commitment_test.go +++ b/modules/core/23-commitment/types/commitment_test.go @@ -21,8 +21,7 @@ type MerkleTestSuite struct { func (suite *MerkleTestSuite) SetupTest() { db := dbm.NewMemDB() - dblog := log.NewNopLogger() - suite.store = rootmulti.NewStore(db, dblog) + suite.store = rootmulti.NewStore(db, log.NewNopLogger()) suite.storeKey = storetypes.NewKVStoreKey("iavlStoreKey") diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index e7badb75c93..740170dbfeb 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -117,7 +117,7 @@ func (k Keeper) ConnectionOpenTry(goCtx context.Context, msg *connectiontypes.Ms } if _, err := k.ConnectionKeeper.ConnOpenTry( - ctx, msg.PreviousConnectionId, msg.Counterparty, msg.DelayPeriod, msg.ClientId, targetClient, + ctx, msg.Counterparty, msg.DelayPeriod, msg.ClientId, targetClient, connectiontypes.ProtoVersionsToExported(msg.CounterpartyVersions), msg.ProofInit, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { @@ -220,7 +220,7 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann } // Perform 04-channel verification - channelID, cap, err := k.ChannelKeeper.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, msg.PreviousChannelId, + channelID, cap, err := k.ChannelKeeper.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, portCap, msg.Channel.Counterparty, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight, ) if err != nil { @@ -266,14 +266,14 @@ func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChann return nil, sdkerrors.Wrap(err, "channel handshake open ack failed") } + // Write channel into state + k.ChannelKeeper.WriteOpenAckChannel(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion, msg.CounterpartyChannelId) + // Perform application logic callback if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannelId, msg.CounterpartyVersion); err != nil { return nil, sdkerrors.Wrap(err, "channel open ack callback failed") } - // Write channel into state - k.ChannelKeeper.WriteOpenAckChannel(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion, msg.CounterpartyChannelId) - return &channeltypes.MsgChannelOpenAckResponse{}, nil } @@ -300,14 +300,14 @@ func (k Keeper) ChannelOpenConfirm(goCtx context.Context, msg *channeltypes.MsgC return nil, sdkerrors.Wrap(err, "channel handshake open confirm failed") } + // Write channel into state + k.ChannelKeeper.WriteOpenConfirmChannel(ctx, msg.PortId, msg.ChannelId) + // Perform application logic callback if err = cbs.OnChanOpenConfirm(ctx, msg.PortId, msg.ChannelId); err != nil { return nil, sdkerrors.Wrap(err, "channel open confirm callback failed") } - // Write channel into state - k.ChannelKeeper.WriteOpenConfirmChannel(ctx, msg.PortId, msg.ChannelId) - return &channeltypes.MsgChannelOpenConfirmResponse{}, nil } @@ -411,12 +411,11 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke // Cache context so that we may discard state changes from callback if the acknowledgement is unsuccessful. cacheCtx, writeFn = ctx.CacheContext() ack := cbs.OnRecvPacket(cacheCtx, msg.Packet, relayer) - // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. - // Events from callback are emitted regardless of acknowledgement success - ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) if ack == nil || ack.Success() { // write application state changes for asynchronous and successful acknowledgements writeFn() + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) } // Set packet acknowledgement only if the acknowledgement is not nil. diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/types/proposal_handle.go index cfbd5fcfa9e..f722c45b308 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle.go @@ -2,6 +2,7 @@ package types import ( "reflect" + "time" "github.com/Finschia/finschia-sdk/codec" sdk "github.com/Finschia/finschia-sdk/types" @@ -70,6 +71,9 @@ func (cs ClientState) CheckSubstituteAndUpdateState( cs.LatestHeight = substituteClientState.LatestHeight cs.ChainId = substituteClientState.ChainId + // set new trusting period based on the substitute client state + cs.TrustingPeriod = substituteClientState.TrustingPeriod + // no validation is necessary since the substitute is verified to be Active // in 02-client. @@ -77,13 +81,15 @@ func (cs ClientState) CheckSubstituteAndUpdateState( } // IsMatchingClientState returns true if all the client state parameters match -// except for frozen height, latest height, and chain-id. +// except for frozen height, latest height, trusting period, chain-id. func IsMatchingClientState(subject, substitute ClientState) bool { // zero out parameters which do not need to match subject.LatestHeight = clienttypes.ZeroHeight() subject.FrozenHeight = clienttypes.ZeroHeight() + subject.TrustingPeriod = time.Duration(0) substitute.LatestHeight = clienttypes.ZeroHeight() substitute.FrozenHeight = clienttypes.ZeroHeight() + substitute.TrustingPeriod = time.Duration(0) subject.ChainId = "" substitute.ChainId = "" // sets both sets of flags to true as these flags have been DEPRECATED, see ADR-026 for more information diff --git a/modules/light-clients/07-tendermint/types/proposal_handle_test.go b/modules/light-clients/07-tendermint/types/proposal_handle_test.go index 459f38187ea..bdd2d98ead5 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle_test.go @@ -141,6 +141,8 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(substitutePath) substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + // update trusting period of substitute client state + substituteClientState.TrustingPeriod = time.Hour * 24 * 7 suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID, substituteClientState) // update substitute a few times @@ -191,6 +193,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { suite.Require().Equal(expectedIterationKey, subjectIterationKey) suite.Require().Equal(newChainID, updatedClient.(*types.ClientState).ChainId) + suite.Require().Equal(time.Hour*24*7, updatedClient.(*types.ClientState).TrustingPeriod) } else { suite.Require().Error(err) suite.Require().Nil(updatedClient) @@ -235,9 +238,15 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }, true, }, { - "not matching, trusting period is different", func() { + "matching, trusting period is different", func() { subjectClientState.TrustingPeriod = time.Duration(time.Hour * 10) substituteClientState.TrustingPeriod = time.Duration(time.Hour * 1) + }, true, + }, + { + "not matching, trust level is different", func() { + subjectClientState.TrustLevel = types.Fraction{2, 3} + substituteClientState.TrustLevel = types.Fraction{1, 3} }, false, }, } diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto index 6ccc0456731..1f38efdc7ef 100644 --- a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto +++ b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto @@ -5,16 +5,34 @@ package ibc.applications.interchain_accounts.controller.v1; option go_package = "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types"; import "ibc/applications/interchain_accounts/controller/v1/controller.proto"; +import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; // Query provides defines the gRPC querier service. service Query { + // InterchainAccount returns the interchain account address for a given owner address on a given connection + rpc InterchainAccount(QueryInterchainAccountRequest) returns (QueryInterchainAccountResponse) { + option (google.api.http).get = + "/ibc/apps/interchain_accounts/controller/v1/owners/{owner}/connections/{connection_id}"; + } + // Params queries all parameters of the ICA controller submodule. rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/ibc/apps/interchain_accounts/controller/v1/params"; } } +// QueryInterchainAccountRequest is the request type for the Query/InterchainAccount RPC method. +message QueryInterchainAccountRequest { + string owner = 1; + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; +} + +// QueryInterchainAccountResponse the response type for the Query/InterchainAccount RPC method. +message QueryInterchainAccountResponse { + string address = 1; +} + // QueryParamsRequest is the request type for the Query/Params RPC method. message QueryParamsRequest {} diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index 5befdf2fbc3..44e068d6967 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -38,7 +38,12 @@ message MsgTransfer { // Timeout timestamp in absolute nanoseconds since unix epoch. // The timeout is disabled when set to 0. uint64 timeout_timestamp = 7 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; + // optional memo + string memo = 8; } // MsgTransferResponse defines the Msg/Transfer response type. -message MsgTransferResponse {} +message MsgTransferResponse { + // sequence number of the transfer packet sent + uint64 sequence = 1; +} diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index acae83530f6..129815ebc0b 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -16,4 +16,6 @@ message FungibleTokenPacketData { string sender = 3; // the recipient address on the destination chain string receiver = 4; + // optional memo + string memo = 5; } diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index 5e3ccf32ff0..75248aeb5b1 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -79,9 +79,8 @@ message MsgChannelOpenTry { option (gogoproto.goproto_getters) = false; string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; - // in the case of crossing hello's, when both chains call OpenInit, we need - // the channel identifier of the previous channel in state INIT - string previous_channel_id = 2 [(gogoproto.moretags) = "yaml:\"previous_channel_id\""]; + // Deprecated: this field is unused. Crossing hello's are no longer supported in core IBC. + string previous_channel_id = 2 [deprecated = true, (gogoproto.moretags) = "yaml:\"previous_channel_id\""]; // NOTE: the version field within the channel has been deprecated. Its value will be ignored by core IBC. Channel channel = 3 [(gogoproto.nullable) = false]; string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""]; diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto index 9d386592fa9..2ec41ed0c65 100644 --- a/proto/ibc/core/client/v1/client.proto +++ b/proto/ibc/core/client/v1/client.proto @@ -7,6 +7,7 @@ option go_package = "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; import "cosmos/upgrade/v1beta1/upgrade.proto"; +import "cosmos_proto/cosmos.proto"; // IdentifiedClientState defines a client state with an additional client // identifier field. @@ -41,7 +42,8 @@ message ClientConsensusStates { // handler may fail if the subject and the substitute do not match in client and // chain parameters (with exception to latest height, frozen height, and chain-id). message ClientUpdateProposal { - option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; // the title of the update proposal string title = 1; // the description of the proposal @@ -56,9 +58,10 @@ message ClientUpdateProposal { // UpgradeProposal is a gov Content type for initiating an IBC breaking // upgrade. message UpgradeProposal { - option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; - option (gogoproto.equal) = true; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.equal) = true; + option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; string title = 1; string description = 2; diff --git a/proto/ibc/core/connection/v1/tx.proto b/proto/ibc/core/connection/v1/tx.proto index 1ca0b404000..b2fea632c36 100644 --- a/proto/ibc/core/connection/v1/tx.proto +++ b/proto/ibc/core/connection/v1/tx.proto @@ -49,14 +49,13 @@ message MsgConnectionOpenTry { option (gogoproto.goproto_getters) = false; string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - // in the case of crossing hello's, when both chains call OpenInit, we need - // the connection identifier of the previous connection in state INIT - string previous_connection_id = 2 [(gogoproto.moretags) = "yaml:\"previous_connection_id\""]; - google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""]; - Counterparty counterparty = 4 [(gogoproto.nullable) = false]; - uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; - repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""]; - ibc.core.client.v1.Height proof_height = 7 + // Deprecated: this field is unused. Crossing hellos are no longer supported in core IBC. + string previous_connection_id = 2 [deprecated = true, (gogoproto.moretags) = "yaml:\"previous_connection_id\""]; + google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""]; + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; + repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""]; + ibc.core.client.v1.Height proof_height = 7 [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; // proof of the initialization the connection on Chain A: `UNITIALIZED -> // INIT` diff --git a/testing/chain.go b/testing/chain.go index 382c42a2522..9593b6c0730 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -104,6 +104,7 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va amount, ok := sdk.NewIntFromString("10000000000000000000") require.True(t, ok) + // add sender account balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)), @@ -573,3 +574,9 @@ func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabili return cap } + +// GetTimeoutHeight is a convenience function which returns a IBC packet timeout height +// to be used for testing. It returns the current IBC height + 100 blocks +func (chain *TestChain) GetTimeoutHeight() clienttypes.Height { + return clienttypes.NewHeight(clienttypes.ParseChainID(chain.ChainID), uint64(chain.GetContext().BlockHeight())+100) +} diff --git a/testing/coordinator.go b/testing/coordinator.go index 0799a3eaaca..e37795aa412 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -194,42 +194,3 @@ func (coord *Coordinator) CommitNBlocks(chain *TestChain, n uint64) { coord.IncrementTime() } } - -// ConnOpenInitOnBothChains initializes a connection on both endpoints with the state INIT -// using the OpenInit handshake call. -func (coord *Coordinator) ConnOpenInitOnBothChains(path *Path) error { - if err := path.EndpointA.ConnOpenInit(); err != nil { - return err - } - - if err := path.EndpointB.ConnOpenInit(); err != nil { - return err - } - - if err := path.EndpointA.UpdateClient(); err != nil { - return err - } - - return path.EndpointB.UpdateClient() -} - -// ChanOpenInitOnBothChains initializes a channel on the source chain and counterparty chain -// with the state INIT using the OpenInit handshake call. -func (coord *Coordinator) ChanOpenInitOnBothChains(path *Path) error { - // NOTE: only creation of a capability for a transfer or mock port is supported - // Other applications must bind to the port in InitGenesis or modify this code. - - if err := path.EndpointA.ChanOpenInit(); err != nil { - return err - } - - if err := path.EndpointB.ChanOpenInit(); err != nil { - return err - } - - if err := path.EndpointA.UpdateClient(); err != nil { - return err - } - - return path.EndpointB.UpdateClient() -} diff --git a/testing/endpoint.go b/testing/endpoint.go index 67e1b23b7ec..38b0cf03330 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -181,8 +181,7 @@ func (endpoint *Endpoint) ConnOpenTry() error { counterpartyClient, proofClient, proofConsensus, consensusHeight, proofInit, proofHeight := endpoint.QueryConnectionHandshakeProof() msg := connectiontypes.NewMsgConnectionOpenTry( - "", endpoint.ClientID, // does not support handshake continuation - endpoint.Counterparty.ConnectionID, endpoint.Counterparty.ClientID, + endpoint.ClientID, endpoint.Counterparty.ConnectionID, endpoint.Counterparty.ClientID, counterpartyClient, endpoint.Counterparty.Chain.GetPrefix(), []*connectiontypes.Version{ConnectionVersion}, endpoint.ConnectionConfig.DelayPeriod, proofInit, proofClient, proofConsensus, proofHeight, consensusHeight, @@ -295,7 +294,7 @@ func (endpoint *Endpoint) ChanOpenTry() error { proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) msg := channeltypes.NewMsgChannelOpenTry( - endpoint.ChannelConfig.PortID, "", // does not support handshake continuation + endpoint.ChannelConfig.PortID, endpoint.ChannelConfig.Version, endpoint.ChannelConfig.Order, []string{endpoint.ConnectionID}, endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID, endpoint.Counterparty.ChannelConfig.Version, proof, height, diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 0a2de262f8b..365ae935615 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -653,7 +653,7 @@ func (app *SimApp) LoadHeight(height int64) error { func (app *SimApp) ModuleAccountAddrs() map[string]bool { modAccAddrs := make(map[string]bool) for acc := range maccPerms { - // do not add mock module to blocked addresses + // do not add the following modules to blocked addresses // this is only used for testing if acc == ibcmock.ModuleName { continue diff --git a/testing/values.go b/testing/values.go index c1ec7042941..034712315f2 100644 --- a/testing/values.go +++ b/testing/values.go @@ -48,7 +48,9 @@ var ( // Default params variables used to create a TM client DefaultTrustLevel ibctmtypes.Fraction = ibctmtypes.DefaultTrustLevel - TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + + TestAccAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs" + TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) UpgradePath = []string{"upgrade", "upgradedIBCState"}